Skip to content

Commit 0b8afb6

Browse files
authored
feat: added [algo-01] (#6)
* feat: added algo-01 definition and text solution * refactor(algo-py): rename src to solutions * feat: added python solution * feat: added go solution * refactor: added typescript solution * fix: fix recursive call * feat: added rust solution * refactor: added todos * feat: update cmake to execute all tests * feat: added c++ solution * refactor: use fixed size directions * ci: fix python unittest command * fix: remove static from cost * chore: remove jest coverage * fix: rust linting
1 parent 1842e81 commit 0b8afb6

26 files changed

+723
-27
lines changed

.github/workflows/algo-py.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ jobs:
2323
uses: actions/setup-python@v2
2424
with:
2525
python-version: '3.x'
26-
- run: python -m unittest discover -s packages/algo-py/src
26+
- run: python -m unittest discover -s packages/algo-py/solutions

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
.DS_STORE
2+
.vscode

PROBLEMS.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ Output: `int`
1515

1616
- Use a hash map to keep track of occurrences for each item
1717
- keep track of the item with most occurrences
18-
- Time Complexity: `O(n)`
19-
- Auxiliary space: `O(n)`
2018

19+
**Time Complexity:** `O(n)` -> We need to check the whole array
20+
**Auxiliary space:** `O(n)` -> It's possible that the array has all unique numbers
21+
22+
### `[algo-01]` Minesweeper Game `[medium]`
23+
24+
Problem description can be found at [leetcode](https://leetcode.com/problems/minesweeper/)
25+
26+
### Solution
27+
- First check if the cell at the `click` is a mine.
28+
- If `click` position is not a mine, perform DFS from the click position, searching for `empty` cells `"E"`
29+
- If there is a mine in the surroundings of the cell i.e. 1 cell in all 8 directions, stop DFS and set the sell with the number of mines found. If there are no mines, mark the cell with `"B"` and continue DFS
30+
31+
**Time Complexity:** `O(m * n)` -> Worst case we will visit every cell
32+
**Auxiliary space:** `O(m + n)` -> Since we are doing `DFS`, at some point our que could contain all the items from the a cell's row and all the items from a the column.

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,9 @@ Ideally what we want:
6565
- A good description with examples, images or a link to `leetcode`, `hackerank` or similar.
6666
- All PR must be submitted with **tests**
6767
- If possible, you might want to provide the solutions in all the different languages.
68+
69+
70+
## TODO
71+
- [ ] Setup linting CI jobs
72+
- [ ] Add GTest sources to repo
73+
- [ ] Fix type check in vscode for algo-cpp and gtest

packages/algo-cpp/CMakeLists.txt

+5-10
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,11 @@ FetchContent_Declare(
1111
)
1212
FetchContent_MakeAvailable(googletest)
1313

14+
# file(GLOB_RECURSE sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
1415

15-
enable_testing()
16-
add_executable(
17-
algo_00
18-
algo_00.cpp
19-
)
20-
target_link_libraries(
21-
algo_00
22-
gtest_main
23-
)
16+
add_executable(tests main.cpp algo_00.cpp algo_01.cpp)
2417

18+
target_link_libraries(tests gtest_main)
19+
enable_testing()
2520
include(GoogleTest)
26-
gtest_discover_tests(algo_00)
21+
gtest_discover_tests(tests)

packages/algo-cpp/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
test:
44
cmake -S . -B build
55
cmake --build build
6-
cd build && ctest -V
6+
cd build && ./tests
77

packages/algo-cpp/algo_00.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <gtest/gtest.h>
22
#include <unordered_map>
3+
34
using namespace std;
45

56
int most_frequent(int arr[], int n)

packages/algo-cpp/algo_01.cpp

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#include <vector>
2+
#include <queue>
3+
#include <gtest/gtest.h>
4+
5+
using namespace std;
6+
7+
const int DIRECTIONS[8][2] = {
8+
{-1, -1},
9+
{-1, 0},
10+
{-1, 1},
11+
{0, -1},
12+
{0, 1},
13+
{1, -1},
14+
{1, 0},
15+
{1, 1},
16+
};
17+
18+
bool is_valid_index(int i, int j, int m, int n);
19+
int get_adjacent_mines(vector<vector<char> > &board, int i, int j, int m, int n);
20+
void dfs(vector<vector<char> > &board, int i, int j, int m, int n);
21+
22+
vector<vector<char> > minesweeper(vector<vector<char> > &board, vector<int> &click)
23+
{
24+
int clickX = click[0], clickY = click[1];
25+
26+
if (board[clickX][clickY] == 'M')
27+
{
28+
board[clickX][clickY] = 'X';
29+
return board;
30+
}
31+
32+
board[clickX][clickY] = 'B';
33+
int m = board.size(), n = board[0].size();
34+
queue<int> q;
35+
q.push(clickX * n + clickY);
36+
37+
while (!q.empty())
38+
{
39+
int point = q.front();
40+
q.pop();
41+
int px = point / n, py = point % n;
42+
int mines = get_adjacent_mines(board, px, py, m, n);
43+
if (mines > 0)
44+
{
45+
board[px][py] = mines + '0';
46+
}
47+
else
48+
{
49+
for (int i = 0; i < 8; i++)
50+
{
51+
int x = px + DIRECTIONS[i][0];
52+
int y = py + DIRECTIONS[i][1];
53+
if (is_valid_index(x, y, m, n) && board[x][y] == 'E')
54+
{
55+
board[x][y] = 'B';
56+
q.push(x * n + y);
57+
}
58+
}
59+
}
60+
}
61+
62+
return board;
63+
}
64+
65+
vector<vector<char> > minesweeper_dfs(vector<vector<char> > &board, vector<int> &click)
66+
{
67+
int clickX = click[0], clickY = click[1];
68+
int m = board.size(), n = board[0].size();
69+
if (board[clickX][clickY] == 'M')
70+
{
71+
board[clickX][clickY] = 'X';
72+
}
73+
else
74+
{
75+
dfs(board, clickX, clickY, m, n);
76+
}
77+
return board;
78+
}
79+
80+
void dfs(vector<vector<char> > &board, int i, int j, int m, int n)
81+
{
82+
if (!is_valid_index(i, j, m, n) || board[i][j] != 'E')
83+
{
84+
return;
85+
}
86+
87+
int mines = get_adjacent_mines(board, i, j, m, n);
88+
if (mines > 0)
89+
{
90+
board[i][j] = mines + '0';
91+
}
92+
else
93+
{
94+
board[i][j] = 'B';
95+
for (int d = 0; d < 8; d++)
96+
{
97+
dfs(board, i + DIRECTIONS[d][0], j + DIRECTIONS[d][1], m, n);
98+
}
99+
}
100+
}
101+
102+
bool is_valid_index(int i, int j, int m, int n)
103+
{
104+
return 0 <= i && i < m && 0 <= j && j < n;
105+
}
106+
107+
int get_adjacent_mines(vector<vector<char> > &board, int i, int j, int m, int n)
108+
{
109+
int count = 0;
110+
for (int d = 0; d < 8; d++)
111+
{
112+
int x = i + DIRECTIONS[d][0];
113+
int y = j + DIRECTIONS[d][1];
114+
if (is_valid_index(x, y, m, n) && board[x][y] == 'M')
115+
{
116+
count++;
117+
}
118+
}
119+
return count;
120+
}
121+
122+
TEST(Minesweeper, UpdatesBoard)
123+
{
124+
vector<vector<char> > input{
125+
{'E', 'E', 'E', 'E', 'E'},
126+
{'E', 'E', 'M', 'E', 'E'},
127+
{'E', 'E', 'E', 'E', 'E'},
128+
{'E', 'E', 'E', 'E', 'E'}};
129+
vector<vector<char> > expected{
130+
{'B', '1', 'E', '1', 'B'},
131+
{'B', '1', 'M', '1', 'B'},
132+
{'B', '1', '1', '1', 'B'},
133+
{'B', 'B', 'B', 'B', 'B'}};
134+
135+
vector<int> click{3, 0};
136+
minesweeper(input, click);
137+
for (int i = 0; i < 4; i++)
138+
{
139+
for (int j = 0; j < 5; j++)
140+
{
141+
EXPECT_EQ(input[i][j], expected[i][j]);
142+
}
143+
}
144+
}
145+
146+
TEST(MinesweeperDFS, UpdatesBoard)
147+
{
148+
vector<vector<char> > input{
149+
{'E', 'E', 'E', 'E', 'E'},
150+
{'E', 'E', 'M', 'E', 'E'},
151+
{'E', 'E', 'E', 'E', 'E'},
152+
{'E', 'E', 'E', 'E', 'E'}};
153+
vector<vector<char> > expected{
154+
{'B', '1', 'E', '1', 'B'},
155+
{'B', '1', 'M', '1', 'B'},
156+
{'B', '1', '1', '1', 'B'},
157+
{'B', 'B', 'B', 'B', 'B'}};
158+
159+
vector<int> click{3, 0};
160+
minesweeper_dfs(input, click);
161+
for (int i = 0; i < 4; i++)
162+
{
163+
for (int j = 0; j < 5; j++)
164+
{
165+
EXPECT_EQ(input[i][j], expected[i][j]);
166+
}
167+
}
168+
}

packages/algo-cpp/main.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <gtest/gtest.h>
2+
3+
int main(int argc, char *argv[])
4+
{
5+
::testing::InitGoogleTest(&argc, argv);
6+
return RUN_ALL_TESTS();
7+
}

packages/algo-go/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ help: ## this help output
66
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
77

88
test: ## runs tests
9-
go test ./...
9+
go test ./... -v

packages/algo-go/solutions/algo-01.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package solutions
2+
3+
import "container/list"
4+
5+
var DIRECTIONS = [8][2]int{
6+
{-1, -1},
7+
{-1, 0},
8+
{-1, 1},
9+
{0, -1},
10+
{0, 1},
11+
{1, -1},
12+
{1, 0},
13+
{1, 1},
14+
}
15+
16+
func Minesweeper(
17+
board [][]byte,
18+
click []int,
19+
) [][]byte {
20+
clickX, clickY := click[0], click[1]
21+
if board[clickX][clickY] == 'M' {
22+
board[clickX][clickY] = 'X'
23+
return board
24+
}
25+
26+
board[clickX][clickY] = 'B'
27+
m, n := len(board), len(board[0])
28+
queue := list.New()
29+
queue.PushBack(clickX*n + clickY)
30+
31+
var i, j int
32+
for queue.Len() > 0 {
33+
point := queue.Front()
34+
queue.Remove(point)
35+
px, py := point.Value.(int)/n, point.Value.(int)%n
36+
mines := getAdjacentMines(board, px, py, m, n)
37+
if mines > 0 {
38+
board[px][py] = byte(mines + '0')
39+
} else {
40+
for _, direction := range DIRECTIONS {
41+
i, j = px+direction[0], py+direction[1]
42+
if isValidIndex(i, j, m, n) && board[i][j] == 'E' {
43+
board[i][j] = 'B'
44+
queue.PushBack(i*n + j)
45+
}
46+
}
47+
}
48+
}
49+
return board
50+
}
51+
52+
func MinesweeperDFS(
53+
board [][]byte,
54+
click []int,
55+
) [][]byte {
56+
clickX, clickY := click[0], click[1]
57+
m, n := len(board), len(board[0])
58+
if board[clickX][clickY] == 'M' {
59+
board[clickX][clickY] = 'X'
60+
} else {
61+
dfs(board, clickX, clickY, m, n)
62+
}
63+
return board
64+
}
65+
66+
func dfs(board [][]byte, i, j, m, n int) {
67+
if !isValidIndex(i, j, m, n) || board[i][j] != 'E' {
68+
return
69+
}
70+
71+
mines := getAdjacentMines(board, i, j, m, n)
72+
if mines > 0 {
73+
board[i][j] = byte(mines + '0')
74+
} else {
75+
board[i][j] = 'B'
76+
for _, direction := range DIRECTIONS {
77+
dfs(board, i+direction[0], j+direction[1], m, n)
78+
}
79+
}
80+
}
81+
82+
func getAdjacentMines(board [][]byte, x, y, m, n int) int {
83+
count := 0
84+
var i, j int
85+
for _, direction := range DIRECTIONS {
86+
i, j = x+direction[0], y+direction[1]
87+
if isValidIndex(i, j, m, n) && board[i][j] == 'M' {
88+
count++
89+
}
90+
}
91+
return count
92+
}
93+
94+
func isValidIndex(i, j, m, n int) bool {
95+
return 0 <= i && i < m && 0 <= j && j < n
96+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package solutions
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
var (
9+
input = [][]byte{
10+
{'E', 'E', 'E', 'E', 'E'},
11+
{'E', 'E', 'M', 'E', 'E'},
12+
{'E', 'E', 'E', 'E', 'E'},
13+
{'E', 'E', 'E', 'E', 'E'},
14+
}
15+
expected = [][]byte{
16+
{'B', '1', 'E', '1', 'B'},
17+
{'B', '1', 'M', '1', 'B'},
18+
{'B', '1', '1', '1', 'B'},
19+
{'B', 'B', 'B', 'B', 'B'},
20+
}
21+
)
22+
23+
func TestMineSweeper(t *testing.T) {
24+
if !reflect.DeepEqual(expected, Minesweeper(input, []int{3, 0})) {
25+
t.Error("Failed")
26+
}
27+
}
28+
29+
func TestMineSweeperDFS(t *testing.T) {
30+
if !reflect.DeepEqual(expected, MinesweeperDFS(input, []int{3, 0})) {
31+
t.Error("Failed")
32+
}
33+
}

packages/algo-py/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.PHONY: test
22

3-
test: python3 -m unittest discover -s src
3+
test:
4+
python3 -m unittest discover -s solutions -p '*_test.py' -v
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .algo_00 import *
2+
from .algo_01 import *

0 commit comments

Comments
 (0)