Solar System Explorer: Notebook Version

Part 2 of 3 — The same class-based implementation, structured as a step-by-step notebook for classroom use

This version of the Solar System Explorer is designed to be worked through cell by cell, so you can see the effect of each piece before adding the next. Run each cell in order.


Cell 1: The Base Class

using System;

public abstract class CelestialLocation
{
    public string Name { get; set; }
    public string FunFact { get; set; }
    
    public CelestialLocation(string name, string funFact)
    {
        Name = name;
        FunFact = funFact;
    }
    
    public virtual string GetInfo()
    {
        return $"{Name}: {FunFact}";
    }
    
    public abstract string GetArrivalMessage();
    public abstract string GetDepartureMessage();
}

Console.WriteLine("CelestialLocation base class defined.");
Console.WriteLine("Note: We can't create a CelestialLocation directly — it's abstract.");
Console.WriteLine("We can only create subclasses that fill in the abstract methods.");

Cell 2: The Planet Subclass

public class Planet : CelestialLocation
{
    public double DistanceFromSun { get; set; }
    public bool HasRings { get; set; }
    
    public Planet(string name, double distanceFromSun, bool hasRings, string funFact) 
        : base(name, funFact)
    {
        DistanceFromSun = distanceFromSun;
        HasRings = hasRings;
    }
    
    public override string GetInfo()
    {
        string ringsInfo = HasRings ? "Has magnificent rings!" : "No rings, how ordinary.";
        return $"{base.GetInfo()}\nDistance from Sun: {DistanceFromSun} million km\n{ringsInfo}";
    }
    
    public override string GetArrivalMessage()
    {
        return $"Touchdown on planet {Name}! Gravity stabilizers engaged. " +
               $"{(HasRings ? "Wow, those rings look even better up close!" : "Clear skies for landing!")}";
    }
    
    public override string GetDepartureMessage()
    {
        return $"Blasting off from {Name}'s gravitational pull! Engaging warp speed in 3...2...1...";
    }
}

// Test it immediately
Planet mars = new Planet("Mars", 227.9, false, 
    "Home to Olympus Mons, the tallest mountain in the solar system!");

Console.WriteLine("=== Planet Test ===");
Console.WriteLine(mars.GetInfo());
Console.WriteLine();
Console.WriteLine(mars.GetArrivalMessage());
Console.WriteLine(mars.GetDepartureMessage());

Cell 3: The Moon Subclass

public class Moon : CelestialLocation
{
    public string ParentPlanet { get; set; }
    public bool HasWater { get; set; }
    
    public Moon(string name, string parentPlanet, bool hasWater, string funFact) 
        : base(name, funFact)
    {
        ParentPlanet = parentPlanet;
        HasWater = hasWater;
    }
    
    public override string GetInfo()
    {
        string waterInfo = HasWater 
            ? "Contains water - potential for space swimming!" 
            : "No water detected - bring your own beverages.";
        return $"{base.GetInfo()}\nOrbits: {ParentPlanet}\n{waterInfo}";
    }
    
    public override string GetArrivalMessage()
    {
        return $"Soft landing on {Name}, a moon of {ParentPlanet}! " +
               "Reduced gravity detected - watch your step or you might accidentally do a backflip!";
    }
    
    public override string GetDepartureMessage()
    {
        return $"Gently pushing off from {Name}'s surface. The views of {ParentPlanet} from here are spectacular!";
    }
}

// Test it
Moon europa = new Moon("Europa", "Jupiter", true, 
    "Has an ocean under its icy crust where space fish might be doing the backstroke!");

Console.WriteLine("=== Moon Test ===");
Console.WriteLine(europa.GetInfo());
Console.WriteLine();
Console.WriteLine(europa.GetArrivalMessage());

Cell 4: The SpaceStation Subclass

public class SpaceStation : CelestialLocation
{
    public string OrbitingBody { get; set; }
    public int CrewCapacity { get; set; }
    
    public SpaceStation(string name, string orbitingBody, int crewCapacity, string funFact) 
        : base(name, funFact)
    {
        OrbitingBody = orbitingBody;
        CrewCapacity = crewCapacity;
    }
    
    public override string GetInfo()
    {
        return $"{base.GetInfo()}\nOrbits: {OrbitingBody}\nCrew Capacity: {CrewCapacity} space explorers";
    }
    
    public override string GetArrivalMessage()
    {
        return $"*Mechanical docking sounds* Connected to {Name} space station! " +
               "Please wait for the airlock to pressurize.";
    }
    
    public override string GetDepartureMessage()
    {
        return $"Disconnecting docking clamps from {Name}. Don't forget to wave to the crew!";
    }
}

// Test it
SpaceStation iss = new SpaceStation("ISS", "Earth", 7, 
    "Astronauts see 16 sunrises every day! Talk about needing coffee.");

Console.WriteLine("=== SpaceStation Test ===");
Console.WriteLine(iss.GetInfo());
Console.WriteLine();
Console.WriteLine(iss.GetArrivalMessage());

Cell 5: Polymorphism in Action

using System.Collections.Generic;

// Create a mixed list using the base type
List<CelestialLocation> locations = new List<CelestialLocation>();
locations.Add(new Planet("Mars", 227.9, false, "Home to Olympus Mons!"));
locations.Add(new Moon("Europa", "Jupiter", true, "Possible subsurface ocean!"));
locations.Add(new SpaceStation("ISS", "Earth", 7, "16 sunrises per day!"));

Console.WriteLine("=== Polymorphism Demo ===");
Console.WriteLine("Calling GetArrivalMessage() on each — notice different output from same method call:\n");

foreach (CelestialLocation location in locations)
{
    Console.WriteLine($"--- {location.Name} ({location.GetType().Name}) ---");
    Console.WriteLine(location.GetArrivalMessage());
    Console.WriteLine();
}

Cell 6: The SolarSystemMap

using System.Linq;

public class SolarSystemMap
{
    private List<CelestialLocation> locations;
    public CelestialLocation CurrentLocation { get; private set; }
    
    public SolarSystemMap()
    {
        locations = new List<CelestialLocation>();
    }
    
    public void AddLocation(CelestialLocation location)
    {
        locations.Add(location);
        if (locations.Count == 1)
        {
            CurrentLocation = location;
            Console.WriteLine($"Starting our adventure at {location.Name}!");
        }
    }
    
    public void DisplayAllLocations()
    {
        Console.WriteLine("\n=== Solar System Map ===");
        
        foreach (var planet in locations.OfType<Planet>())
        {
            string here = planet == CurrentLocation ? " << YOU ARE HERE" : "";
            Console.WriteLine($"[Planet] {planet.Name}{here}");
        }
        foreach (var moon in locations.OfType<Moon>())
        {
            string here = moon == CurrentLocation ? " << YOU ARE HERE" : "";
            Console.WriteLine($"[Moon]   {moon.Name} (orbits {moon.ParentPlanet}){here}");
        }
        foreach (var station in locations.OfType<SpaceStation>())
        {
            string here = station == CurrentLocation ? " << YOU ARE HERE" : "";
            Console.WriteLine($"[Station] {station.Name}{here}");
        }
    }
    
    public bool TravelTo(string destinationName)
    {
        CelestialLocation destination = locations.FirstOrDefault(loc => loc.Name == destinationName);
        
        if (destination == null)
        {
            Console.WriteLine($"Error: {destinationName} not found!");
            return false;
        }
        
        if (destination == CurrentLocation)
        {
            Console.WriteLine($"Already at {destinationName}!");
            return false;
        }
        
        Console.WriteLine($"\n{CurrentLocation.GetDepartureMessage()}");
        Console.WriteLine($"Traveling to {destination.Name}...");
        Console.WriteLine(destination.GetArrivalMessage());
        
        CurrentLocation = destination;
        return true;
    }
}

Console.WriteLine("SolarSystemMap class defined.");

Cell 7: Putting It All Together

SolarSystemMap solarSystem = new SolarSystemMap();

solarSystem.AddLocation(new Planet("Mars", 227.9, false, 
    "Home to Olympus Mons, the tallest mountain in the solar system!"));
solarSystem.AddLocation(new Planet("Saturn", 1433.5, true, 
    "Its rings would make a fabulous hula hoop for a giant!"));
solarSystem.AddLocation(new Moon("Europa", "Jupiter", true, 
    "Has an ocean under its icy crust!"));
solarSystem.AddLocation(new Moon("Titan", "Saturn", false, 
    "Has lakes of liquid methane. Not recommended for swimming."));
solarSystem.AddLocation(new SpaceStation("ISS", "Earth", 7, 
    "Astronauts see 16 sunrises every day!"));

solarSystem.DisplayAllLocations();

Console.WriteLine("\n=== Travel Sequence ===");
solarSystem.TravelTo("Europa");
solarSystem.DisplayAllLocations();

Console.WriteLine();
solarSystem.TravelTo("ISS");
solarSystem.DisplayAllLocations();

Student Exercises

Try these extensions once you've run all the cells above:

  1. Add an AsteroidBelt class — What properties should it have? What would arrival and departure messages look like?

  2. Search by orbiting body — Add a method to SolarSystemMap that returns all locations orbiting a given body. For example, GetLocationsOrbitingBody("Saturn") should return Titan and possibly a space station.

  3. Fuel system — Add a FuelLevel property to SolarSystemMap. Travel costs fuel based on how far apart the locations are. What happens when you run out?

  4. Distance calculator — For planets, calculate approximate distance between two planets based on their DistanceFromSun. What additional information would you need to make this realistic?

  5. Nearby locations — Add a method that shows which locations are "close" to the current one (within some threshold). Define "close" however makes sense for your chosen measurement.


See Solar System 1 of 3 for the full narrative version, or Solar System Bonus 3 of 3 for advanced extensions.

Built with LogoFlowershow