Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,109 @@ jobs:
- name: Install Dependencies
run: npm install

- name: Validate README problem stats
run: |
node <<'EOF'
const fs = require('node:fs');
const path = require('node:path');

const summaryFile = process.env.GITHUB_STEP_SUMMARY;
const repoRoot = process.cwd();
const problemsDir = path.join(repoRoot, 'Problems');
const readmePath = path.join(repoRoot, 'README.md');

const appendSummary = (line = '') => {
fs.appendFileSync(summaryFile, `${line}\n`);
};

const errors = [];

const problemFolders = fs
.readdirSync(problemsDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort((a, b) => a.localeCompare(b));

const implementedCount = problemFolders.length;
const readme = fs.readFileSync(readmePath, 'utf8');

const solvedMatch = readme.match(/Solved:\s*\*\*(\d+)\s+problems\*\*/i);
const easyMatch = readme.match(/🟢\s*Easy:\s*(\d+)/i);
const mediumMatch = readme.match(/🟠\s*Medium:\s*(\d+)/i);
const hardMatch = readme.match(/🔴\s*Hard:\s*(\d+)/i);

const solvedCount = solvedMatch ? Number.parseInt(solvedMatch[1], 10) : Number.NaN;
const easyCount = easyMatch ? Number.parseInt(easyMatch[1], 10) : Number.NaN;
const mediumCount = mediumMatch ? Number.parseInt(mediumMatch[1], 10) : Number.NaN;
const hardCount = hardMatch ? Number.parseInt(hardMatch[1], 10) : Number.NaN;

if (!Number.isFinite(solvedCount)) {
errors.push('Could not parse "Solved: **X problems**" in `README.md`.');
}

if (![easyCount, mediumCount, hardCount].every(Number.isFinite)) {
errors.push('Could not parse difficulty counts for Easy/Medium/Hard in `README.md`.');
}

if (Number.isFinite(solvedCount) && solvedCount !== implementedCount) {
errors.push(
`Mismatch between implemented problem folders (${implementedCount}) and "Solved" count (${solvedCount}).`,
);
}

if ([easyCount, mediumCount, hardCount].every(Number.isFinite) && Number.isFinite(solvedCount)) {
const difficultySum = easyCount + mediumCount + hardCount;

if (difficultySum !== solvedCount) {
errors.push(
`Mismatch in difficulty sum: Easy + Medium + Hard = ${difficultySum}, but "Solved" is ${solvedCount}.`,
);
}
}

const tablePathMatches = [...readme.matchAll(/\.\/Problems\/([^/]+)\/README\.md/g)].map(
(match) => match[1],
);
const tableFolderSet = new Set(tablePathMatches);

const missingInTable = problemFolders.filter((folder) => !tableFolderSet.has(folder));
const extraInTable = [...tableFolderSet].filter((folder) => !problemFolders.includes(folder));

if (missingInTable.length > 0) {
errors.push(`Problems missing in README table: ${missingInTable.join(', ')}.`);
}

if (extraInTable.length > 0) {
errors.push(`README table references non-existing problem folders: ${extraInTable.join(', ')}.`);
}

appendSummary('## 📚 README Consistency Checks');
appendSummary('');
appendSummary(`- Implemented problem folders: **${implementedCount}**`);
appendSummary(
`- README solved count: **${Number.isFinite(solvedCount) ? solvedCount : 'unreadable'}**`,
);
appendSummary(
`- README difficulty counts: 🟢 ${Number.isFinite(easyCount) ? easyCount : 'unreadable'}, 🟠 ${Number.isFinite(mediumCount) ? mediumCount : 'unreadable'}, 🔴 ${Number.isFinite(hardCount) ? hardCount : 'unreadable'}`,
);
appendSummary(`- README table entries: **${tableFolderSet.size}**`);
appendSummary('');

if (errors.length > 0) {
appendSummary('> [!CAUTION]');
appendSummary('> ❌ README consistency checks failed:');
appendSummary('');
for (const error of errors) {
appendSummary(`- ${error}`);
}

process.exit(1);
}

appendSummary('> [!NOTE]');
appendSummary('> ✅ README consistency checks passed.');
EOF

- name: Test problems solutions
run: npm run test:ci

Expand Down Expand Up @@ -104,4 +207,4 @@ jobs:

echo "" >> "$GITHUB_STEP_SUMMARY"
echo "</details>" >> "$GITHUB_STEP_SUMMARY"
fi
fi
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"source.organizeImports.biome": "explicit"
},
"cSpell.language": "en,de-DE",
"cSpell.words": ["amira", "bella", "distanz", "fibonacci"]
"cSpell.words": ["amira", "bella", "distanz", "fibonacci", "symbole"]
}
67 changes: 67 additions & 0 deletions Problems/Reach-Exit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Reach Exit

Your given a 2D maze and you need to check if it is possible to reach the exit.

## Infos which are maybe needed

Each cell is one of the following symbols:

- `.` Empty space (Can walk here)
- `#` wall (Can not walk here)
- `@` start position
- `E` exit

You can only move up, left, right, down

## Documentation

### Solution Idea

Start at the given position and explore **all possible directions** from there.

For each new position:

- check whether it is the goal
- if it is not the goal, continue exploring from that position in **all possible directions**
- do **not** visit positions that have already been visited

Repeat this process until:

- the goal is found → return `true`
- there are no more positions left to explore → return `false`

---

### [Implementation](./solver.ts)

The program first validates the given input.
If the input is valid, it searches for the start position in the maze.

Once the start position is found, the program calls the `move` function with this position.
This function determines whether the end of the maze is reachable.

The `move` function works recursively:

1. It first checks whether the current position is the end position.
- If this is the case, the function returns true.

2. If the current position is not the end, the function checks all possible directions.

3. For each direction, the program verifies that:
- the position is inside the maze,
- the position has not been visited yet,
- the position is not a wall.

4. If all conditions are satisfied, the function calls itself recursively with the new position.

By doing this, the algorithm explores all reachable positions in the maze.
If one of the recursive calls reaches the end position, the function returns true.

If all possible paths have been explored and none reach the end, the function returns false.

---

### Information's

This problem comes from the newsletter [Sloth Bytes](https://slothbytes.beehiiv.com).
[Post](https://slothbytes.beehiiv.com/p/your-projects-probably-suck) from November 12, 2025.
28 changes: 28 additions & 0 deletions Problems/Reach-Exit/reach-exit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";
import canReachExit from "./solver";

describe("The Maze can be exit", () => {
const mazeOne = [".@.", ".#E", "..."];

it("should return true for the first maze", () => {
expect(canReachExit(mazeOne)).toEqual(true);
});

const mazeTwo = ["@#E"];

it("should return false for the second maze", () => {
expect(canReachExit(mazeTwo)).toEqual(false);
});

const mazeThree = ["@.#.", "..#E", "####"];

it("should return false for the third maze", () => {
expect(canReachExit(mazeThree)).toEqual(false);
});

const mazeFour = ["@...", ".###", "...E"];

it("should return true for the fourth maze", () => {
expect(canReachExit(mazeFour)).toEqual(true);
});
});
65 changes: 65 additions & 0 deletions Problems/Reach-Exit/solver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const symbole = {
WALL: "#",
EMPTY: ".",
START: "@",
END: "E",
};

export default function canReachExit(givenMaze: string[]): boolean {
const maze: string[][] = givenMaze.map((val) => val.split(""));
const start = encodePos(
maze[maze.findIndex((val) => val.some((v) => v === "@"))].indexOf("@"),
maze.findIndex((val) => val.some((v) => v === "@"))
); // You can do that way better, I know...

// Input checking
if (maze.reduce((pre, cur) => (!pre ? false : cur.length !== maze[0].length), true)) return false;

const visitedPlaces: string[] = [];

function move(string: string): boolean {
const checkPossibleMove = (y: number, x: number) => {
if (
y >= 0 &&
y < maze.length &&
x >= 0 &&
x < maze[0].length &&
(maze[y][x] === symbole.END || maze[y][x] === symbole.EMPTY)
) {
const newPos = encodePos(x, y);

if (!visitedPlaces.some((val) => val === newPos)) {
return move(newPos);
} else return false;
}
};

const { x, y } = decodePos(string);

if (maze[y][x] === symbole.END) return true;

visitedPlaces.push(string);

if (
checkPossibleMove(y + 1, x) ||
checkPossibleMove(y, x + 1) ||
checkPossibleMove(y - 1, x) ||
checkPossibleMove(y, x - 1)
)
return true;

return false;
}

return move(start);
}

function encodePos(x: number, y: number): string {
return `${x}|${y}`;
}

function decodePos(string: string): { x: number; y: number } {
const [x, y] = string.split("|").map((val) => Number(val));

return { x, y };
}
64 changes: 44 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
# Problem-Solving

This repository is about solving coding-problems. It contains famous (or not that famous) problems with my solution. I ranked the problems as <span style="color:green">Easy</span>, <span style="color:orange">Medium</span> and <span style="color:red">Hard</span>. These ranks reflect the time I invested in the problem and how difficult it felt to me.
This repository is about solving coding-problems. It contains famous (or not that famous) problems with my solution (Dennis Bauer). I ranked the problems as 🟢 Easy, 🟠 Medium and 🔴 Hard. These ranks reflect the time I invested in the problem and how difficult it felt to me.

# Problem-list

<!-- PROBLEMS_START -->

- [Sudoku-Solver](./Problems/Sudoku-Solver/README.md) (<span style="color:orange">Medium</span>)
- [Upside-Down](./Problems/Upside-Down/README.md) (<span style="color:green">Easy</span>)
- [Fibonacci-Sequence](./Problems/Fibonacci-Sequence/README.md) (<span style="color:green">Easy</span>)
- [Valid-Hex-Code](./Problems/Valid-Hex-Code/README.md) (<span style="color:green">Easy</span>)
- [Quadratisch-Praktisch-Grün](./Problems/Quadratisch-Praktisch-Grün/README.md) (<span style="color:red">Hard</span>)
- [Texthopsen](./Problems/Texthopsen/README.md) (<span style="color:orange">Medium</span>)
- [ExcelSheetColumnTitle](./Problems/ExcelSheetColumnTitle/README.md) (<span style="color:green">Easy</span>)
- [Evaluating-Simple-Algebra](./Problems/Evaluating-Simple-Algebra/README.md) (<span style="color:green">Easy</span>)
- [Trace-The-Path-Of-The-Word](./Problems/Trace-Path-Of-World/README.md) (<span style="color:green">Easy</span>)
- [Sil-ben-tren-nung](./Problems/Sil-ben-tren-nung/README.md) (<span style="color:orange">Medium</span>)
- [Bällebad](./Problems/Bällebad/README.md) (<span style="color:green">Easy</span>)
- [Keyword-Cipher](./Problems/Keyword-Cipher/README.md) (<span style="color:green">Easy</span>)
- [Exit-Maze](./Problems/Exit-Maze/README.md) (<span style="color:green">Easy</span>)
- [Phone-Letter-Combinations](./Problems/Phone-Letter-Combinations/README.md) (<span style="color:green">Easy</span>)
- [Itinerary-In-Alphabetical-Order](./Problems/Itinerary-In-Alphabetical-Order/README.md) (<span style="color:green">Easy</span>)
- [Dependable-Jobs-Schedule](./Problems/Dependable-Jobs-Schedule/README.md) (<span style="color:green">Easy</span>)
- [Nom-Nom-Numbers](./Problems/Nom-Nom-Numbers/README.md) (<span style="color:green">Easy</span>)
- [Shortes-Path-Dijkstra](./Problems/Shortes-Path-Dijkstra/README.md) <span style="color:orange">Medium</span>
- [Sleep-Time](./Problems/Sleep-Time/README.md) (<span style="color:green">Easy</span>)
## Problems

Solved: **20 problems**

🟢 Easy: 14
🟠 Medium: 5
🔴 Hard: 1

| Problem | Difficulty |
| --------------------------------------------------------------------------------------- | ---------- |
| [Sudoku Solver](./Problems/Sudoku-Solver/README.md) | 🟠 Medium |
| [Upside Down](./Problems/Upside-Down/README.md) | 🟢 Easy |
| [Fibonacci Sequence](./Problems/Fibonacci-Sequence/README.md) | 🟢 Easy |
| [Valid Hex Code](./Problems/Valid-Hex-Code/README.md) | 🟢 Easy |
| [Quadratisch Praktisch Grün](./Problems/Quadratisch-Praktisch-Grün/README.md) | 🔴 Hard |
| [Texthopsen](./Problems/Texthopsen/README.md) | 🟠 Medium |
| [Excel Sheet Column Title](./Problems/Excel-Sheet-Column-Title/README.md) | 🟢 Easy |
| [Evaluating Simple Algebra](./Problems/Evaluating-Simple-Algebra/README.md) | 🟢 Easy |
| [Trace The Path Of The Word](./Problems/Trace-Path-Of-Word/README.md) | 🟢 Easy |
| [Sil-ben-tren-nung](./Problems/Sil-ben-tren-nung/README.md) | 🟠 Medium |
| [Bällebad](./Problems/Bällebad/README.md) | 🟢 Easy |
| [Keyword Cipher](./Problems/Keyword-Cipher/README.md) | 🟢 Easy |
| [Exit Maze](./Problems/Exit-Maze/README.md) | 🟢 Easy |
| [Phone Letter Combinations](./Problems/Phone-Letter-Combinations/README.md) | 🟢 Easy |
| [Itinerary In Alphabetical Order](./Problems/Itinerary-In-Alphabetical-Order/README.md) | 🟢 Easy |
| [Dependable Jobs Schedule](./Problems/Dependable-Jobs-Schedule/README.md) | 🟢 Easy |
| [Nom Nom Numbers](./Problems/Nom-Nom-Numbers/README.md) | 🟢 Easy |
| [Shortest Path Dijkstra](./Problems/Shortes-Path-Dijkstra/README.md) | 🟠 Medium |
| [Sleep Time](./Problems/Sleep-Time/README.md) | 🟢 Easy |
| [Reach Exit](./Problems/Reach-Exit/README.md) | 🟢 Easy |

<!-- PROBLEMS_END -->

## npm-Version:

The version indicates how much problems are implemented (cp -> Coding Problem)

## Tools I'm using

| Tool | How I'm using it |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ChatGPT | I use ChatGPT for grammar checks and for making my Markdown prettier. When solving a problem I sometimes reach out to ChatGPT for tips on how I could improve or shorten my code, or for a bit of help if I'm stuck. I **never** let ChatGPT solve a problem for me! |
| VS-Code | I'm using VS Code and all the features it brings. It's my go-to IDE and I couldn't live without it. The feature I use the most is the code completion. I think without it I would be lost. |
| cSpell | cSpell is a VS Code extension that checks spelling mistakes. It's not perfect, but it helps me catch spelling mistakes in my code. |
| Github-Workflow | GitHub Workflows help me when it comes to publishing new versions, updating the version, or testing if a new problem can be merged into the main branch. In a perfect world it would also allow everyone to add their own problems to this list and/or get a copy of the repository without my solutions so they can try to solve them themselves. But for that I would need viewers who are interested in this repository. |
| Vitest | I think Vitest is one of the best testing packages you can have when using TypeScript. It's easy to use, looks really nice when it runs, and has many useful features. I'm really happy with it and it makes my testing life much easier. |
| Typescript | TypeScript is almost always the better solution when using JavaScript. The code completion is great, and writing code with proper types helps you understand what the code actually does. It's just good practice. |
| BiomeJS | BiomeJS is a really good solution for linting and formatting JavaScript. I use it here so every problem has the same format. |
| Github/Git | When it comes to code organization and planning, my go-to tools are GitHub and Git. I really like the feature of creating issues when I find an interesting new problem, and I can use branches when I want to work on separate problems. GitHub also gives me the opportunity to share my code publicly and collaborate with others. |
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading