Space Program is a C# / .NET console application that simulates a rescue mission in a cosmic navigation map. The application generates a random space map, places one to three astronauts, locates the Space Station and calculates each astronaut's safest route through open space, asteroids and space debris.
The project was built as a solution for the SPACE 2026 Technical Assessment provided by Hitachi Solutions Europe (Bulgaria). The main goal is to guide multiple astronauts back to the Space Station by finding the shortest valid path, displaying the route visually and reporting failed missions first when an astronaut cannot reach the destination.
The implementation focuses on clean object-oriented design, swappable pathfinding algorithms, custom validation, structured error handling, console rendering, dynamic map generation and optional SMTP email reporting.
- Project at a glance
- Mission rules
- Implemented features
- Input and output flow
- Pathfinding
- Architecture
- Domain model
- Application flow
- Email reporting
- Validation and error handling
- Tech stack
- Getting started
- Project structure
- License
The application starts by asking the user for the number of rows and columns of the cosmic map. The accepted map size is between 2 and 100 for both dimensions. After validation, a random map is generated and displayed.
Each generated map contains:
| Symbol | Meaning | Walkable | Movement cost |
|---|---|---|---|
S1, S2, S3 |
Astronaut starting positions | Yes | 1 |
F |
Final destination / Space Station | Yes | 1 |
O |
Open Space | Yes | 1 |
D |
Space Debris | Yes | 2 |
X |
Asteroid | No | Not allowed |
* |
Calculated route segment | Output only | - |
The mission service processes every astronaut independently and creates a mission report. Failed missions are displayed first, while successful missions are sorted by total movement cost and then by astronaut name.
The rules implemented in the project follow the assessment requirements:
- The application is a C# / .NET console application.
- The map dimensions must be within the allowed range:
2 >= rows <= 100and2 >= columns <= 100. - The map contains at least one astronaut and at most three astronauts.
- Astronauts can move only in four directions: up, down, left and right.
- Asteroids are blocked cells and cannot be entered.
- The route is shown with
*, without replacing the Astronaut start position or the Space Station. - Unreachable astronauts are reported as failed missions.
- Successful astronauts are ordered by shortest distance / total movement cost.
- Space debris is supported as a weighted cell with movement cost
2. - The active pathfinding algorithm can be swapped through the
IPathFinderabstraction. - The final report can optionally be sent by email using SMTP.
The project does not rely on a hardcoded map. The user enters valid dimensions, and RandomMapGenerator creates a new cosmic environment. It randomly places the Space Station, generates between one and three astronauts and fills the remaining cells with open space, asteroids and space debris.
Every astronaut is processed independently. The application calculates a separate route from each astronaut's current position to the Space Station. A mission report groups successful and failed results.
The active algorithm in Program.cs is DijkstraPathFinder, which supports weighted cells. This allows the project to correctly handle D space debris, where passing through the cell costs 2 instead of 1.
The project also includes BfsPathFinder. Since both algorithms implement IPathFinder, the mission logic does not need to change when the algorithm is replaced.
IPathResultBuilder builder = new PathResultBuilder();
IPathFinder dijkstra = new DijkstraPathFinder(builder);
IPathFinder bfs = new BfsPathFinder(builder);
IMissionService service = new MissionService(dijkstra);TextMapRenderer renders the original map and the mission result maps. It uses ICosmicMap instead of depending directly on the concrete CosmicMap implementation, so the renderer does not need to know how the map stores its cells internally.
After the mission report is rendered, the application asks the user whether the report should be sent by email. If the user presses Y, the application reads and validates the sender Gmail address, Gmail App Password, receiver email and subject. The report body is the rendered mission output.
The main console flow is intentionally simple and user-friendly.
flowchart TD
Start([Start application]) --> Welcome[Show welcome message]
Welcome --> Rows[Read and validate rows]
Rows --> Columns[Read and validate columns]
Columns --> Generate[Generate random cosmic map]
Generate --> RenderInitial[Render generated map]
RenderInitial --> Process[Process astronaut missions]
Process --> RenderReport[Render mission report]
RenderReport --> AskEmail{Send report via email?}
AskEmail -->|Y| EmailInput[Read and validate email data]
EmailInput --> SendEmail[Send report via Gmail SMTP]
AskEmail -->|Any other key| RestartPrompt[Ask for restart]
SendEmail --> RestartPrompt
RestartPrompt -->|TAB| Exit([Exit])
RestartPrompt -->|Any other key| Welcome
Example input flow:
Welcome to the Space Program!
Please enter the map's rows: 5
Please enter the map's columns: 7
Example output format:
Cosmic map:
D X X X X X D
S3 X X D D D D
D D X O X X X
X X O S1 X D X
S2 F D O O O O
Mission failed — Astronaut S3 lost in space!
Astronaut S2 — Shortest path: 1 step.
D X X X X X D
S3 X X D D D D
D D X O X X X
X X O S1 X D X
S2 F D O O O O
Astronaut S1 — Shortest path: 4 steps.
D X X X X X D
S3 X X D D D D
D D X O X X X
X X * S1 X D X
S2 F * O O O O
The application contains two pathfinding implementations.
| Algorithm | Class | Best used for | Handles D weighted debris |
|---|---|---|---|
| Breadth-first search | BfsPathFinder |
Unweighted maps where every passable cell costs 1 |
No |
| Dijkstra shortest path | DijkstraPathFinder |
Weighted maps with different movement costs | Yes |
BfsPathFinder uses a queue, a visited set, and a previous-node dictionary. It is appropriate when every walkable cell has the same cost. It calculates the fewest number of moves, but it does not account for higher movement costs.
DijkstraPathFinder uses a PriorityQueue<Coordinates, double> and calculates the lowest total movement cost. It reads the movement cost from each SpaceObject, which makes the algorithm compatible with SpaceDebris.
flowchart LR
Start[Start coordinate] --> Queue[PriorityQueue]
Queue --> Current[Current lowest-cost coordinate]
Current --> Neighbours[Walkable neighbours]
Neighbours --> Cost[Add neighbour MovementCost]
Cost --> Update{Better distance?}
Update -->|Yes| Save[Update distance and previous]
Save --> Queue
Update -->|No| Queue
Current --> Destination{Destination reached?}
Destination -->|Yes| Result[Build successful MissionResult]
Destination -->|No| Queue
The path itself is reconstructed by PathResultBuilder, using the previous dictionary produced by the selected pathfinding algorithm.
The project is organized around small components with clear responsibilities. Most dependencies are expressed through interfaces, which keeps the core mission flow independent from concrete implementations.
flowchart TB
Program[Program.cs] --> Engine[IEngine / Engine]
Engine --> Reader[IReader]
Engine --> Writer[IWriter]
Engine --> MapGenerator[IMapGenerator]
Engine --> MissionService[IMissionService]
Engine --> Renderer[IMapRenderer]
Engine --> ConsoleKey[IConsoleKey]
Engine --> EmailSender[IEmailSender]
MapGenerator --> Factory[ISpaceObjectFactory]
MapGenerator --> Random[IRandomGenerator]
MapGenerator --> Map[ICosmicMap]
MissionService --> PathFinder[IPathFinder]
PathFinder --> ResultBuilder[IPathResultBuilder]
PathFinder --> Map
Renderer --> Map
Renderer --> Appender[IStringAppender]
EmailSender --> SMTP[Gmail SMTP]
| Area | Design choice |
|---|---|
| Map abstraction | ICosmicMap hides the internal dictionary-based storage |
| Space objects | SpaceObject base class with specific implementations |
| Pathfinding | IPathFinder allows BFS and Dijkstra to be swapped |
| Rendering | IMapRenderer separates report formatting from mission logic |
| Input/output | IReader, IWriter, and IConsoleKey wrap console operations |
IEmailSender isolates SMTP communication |
|
| Validation | DimensionValidator and EmailValidator centralize input validation |
| Errors | Custom exceptions provide user-friendly failure messages |
The map is represented by CosmicMap, which stores coordinates and space objects internally. External components work through the ICosmicMap interface.
classDiagram
class ICosmicMap {
<<interface>>
+int Rows
+int Columns
+Coordinates SpaceStationCoordinates
+ICollection~Coordinates~ AstronautsCoordinates
+GetWalkableNeighbours(Coordinates)
+GetAllCoordinates()
+GetObjectAt(Coordinates)
+TryGetObjectAt(Coordinates, out SpaceObject)
}
class CosmicMap {
-IDictionary~Coordinates, SpaceObject~ Cells
+GetWalkableNeighbours(Coordinates)
+GetAllCoordinates()
+GetObjectAt(Coordinates)
+TryGetObjectAt(Coordinates, out SpaceObject)
}
class Coordinates {
+int Row
+int Column
}
class SpaceObject {
<<abstract>>
+SpaceObjectType ObjectType
+double MovementCost
+bool IsWalkable
+string DisplaySymbol
}
class Astronaut
class SpaceStation
class OpenSpace
class SpaceDebris
class Asteroid
ICosmicMap <|.. CosmicMap
CosmicMap --> Coordinates
CosmicMap --> SpaceObject
SpaceObject <|-- Astronaut
SpaceObject <|-- SpaceStation
SpaceObject <|-- OpenSpace
SpaceObject <|-- SpaceDebris
SpaceObject <|-- Asteroid
Each concrete SpaceObject defines its own behavior:
| Class | Symbol | Walkable | Cost |
|---|---|---|---|
Astronaut |
S1, S2, S3 |
Yes | 1 |
SpaceStation |
F |
Yes | 1 |
OpenSpace |
O |
Yes | 1 |
SpaceDebris |
D |
Yes | 2 |
Asteroid |
X |
No | Infinity / blocked |
Engine coordinates the application. It does not calculate paths directly, render map cells directly, send SMTP commands directly, or generate random objects directly. Instead, it delegates those responsibilities to injected abstractions.
sequenceDiagram
participant User
participant Engine
participant Validator as DimensionValidator
participant Generator as IMapGenerator
participant Mission as IMissionService
participant PathFinder as IPathFinder
participant Renderer as IMapRenderer
participant Email as IEmailSender
User->>Engine: Enter rows and columns
Engine->>Validator: Validate dimensions
Engine->>Generator: GenerateMap(rows, columns)
Generator-->>Engine: ICosmicMap
Engine->>Renderer: Render generated map
Engine->>Mission: ProcessMissions(map)
Mission->>PathFinder: FindPath(map, astronaut, station)
PathFinder-->>Mission: MissionResult
Mission-->>Engine: MissionReport
Engine->>Renderer: Render(map, report)
Engine-->>User: Show mission report
User->>Engine: Press Y to send email
Engine->>Email: SendGmailAsync(...)
Email-->>Engine: Success / exception
| Component | Responsibility |
|---|---|
Engine |
Coordinates the whole application flow |
RandomMapGenerator |
Generates the cosmic map dynamically |
SpaceObjectFactory |
Creates concrete space objects |
MissionService |
Processes astronauts and produces a mission report |
BfsPathFinder |
Calculates shortest path in unweighted maps |
DijkstraPathFinder |
Calculates lowest-cost path in weighted maps |
PathResultBuilder |
Builds successful and failed mission results |
TextMapRenderer |
Converts maps and mission reports to console text |
EmailSender |
Sends rendered reports through Gmail SMTP |
DimensionValidator |
Validates map dimensions |
EmailValidator |
Validates email-related input |
ConsoleReader / ConsoleWriter |
Wrap console input and output |
ConsoleKey |
Converts key presses into UserCommand values |
The email feature is implemented as an optional mission-report export step. It uses SmtpClient with Gmail SMTP settings extracted into constants.
| Setting | Value |
|---|---|
| Host | smtp.gmail.com |
| Port | 587 |
| SSL | true |
| Default credentials | false |
The application expects a Gmail App Password, not the normal Gmail account password. The sender email, App Password, receiver email and subject are requested from the user only when the user chooses to send the report.
The email flow uses these components:
flowchart LR
Engine --> Ask[Ask user: send email?]
Ask -->|Y| Read[Read email input]
Read --> Validate[EmailValidator]
Validate --> Sender[IEmailSender]
Sender --> Gmail[Gmail SMTP]
Gmail --> Result[Success / EmailSendingException]
The SMTP implementation throws EmailSendingException for sending failures. This keeps the infrastructure class independent from the console UI and lets Engine decide what message should be displayed.
Validation is separated from the main flow. Engine repeatedly asks for input until the validators accept it.
DimensionValidator checks that the input is present, numeric, and within the configured bounds.
Minimum size: 2
Maximum size: 100
EmailValidator validates:
| Input | Validation |
|---|---|
| Sender email | Required and must match the Gmail format |
| Gmail App Password | Required |
| Receiver email | Required and must match the Gmail format |
| Subject | Required |
| Body | Required |
Custom exceptions are stored under GCommon/Exceptions and provide focused messages for invalid input, null input, invalid space objects and email sending failures.
| Category | Technology |
|---|---|
| Language | C# |
| Runtime | .NET 8 |
| Application type | Console application |
| Pathfinding | BFS and Dijkstra |
System.Net.Mail / Gmail SMTP |
|
| Architecture style | OOP, interface-based design, SOLID |
You need the following installed locally:
dotnet --version
git --versionThe project targets:
<TargetFramework>net8.0</TargetFramework>git clone https://github.com/krasimirnyv/Assessment-Task-SPACE-2026.git
cd Assessment-Task-SPACE-2026dotnet builddotnet run --project SpaceProgram/SpaceProgram.csprojIf the project is placed directly at the repository root, run:
dotnet runSpaceProgram
├── Algorithms
│ ├── BfsPathFinder.cs
│ ├── DijkstraPathFinder.cs
│ ├── Helpers
│ │ ├── Interfaces
│ │ │ └── IPathResultBuilder.cs
│ │ └── PathResultBuilder.cs
│ └── Interfaces
│ └── IPathFinder.cs
├── Core
│ ├── Engine.cs
│ └── Interfaces
│ └── IEngine.cs
├── Factories
│ ├── Interfaces
│ │ └── ISpaceObjectFactory.cs
│ └── SpaceObjectFactory.cs
├── GCommon
│ ├── Constants
│ ├── Enums
│ ├── Exceptions
│ └── Validations
├── IO
│ ├── ConsoleKey.cs
│ ├── ConsoleReader.cs
│ ├── ConsoleWriter.cs
│ ├── EmailSender.cs
│ ├── Enums
│ └── Interfaces
├── Models
│ ├── Coordinates.cs
│ ├── CosmicMap.cs
│ ├── Interfaces
│ │ └── ICosmicMap.cs
│ └── SpaceObjects
├── Rendering
│ ├── Interfaces
│ │ └── IMapRenderer.cs
│ └── TextMapRenderer.cs
├── Services
│ ├── Interfaces
│ ├── MissionService.cs
│ ├── Models
│ └── RandomMapGenerator.cs
├── Utilities
│ ├── Ambience.cs
│ ├── RandomGenerator.cs
│ ├── StringAppender.cs
│ └── Interfaces
├── Program.cs
└── SpaceProgram.csproj
This project is licensed under the MIT License.
You are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, provided that the copyright notice and this permission notice are included in all copies or substantial portions of the Software.
See the LICENSE file for the full text.