-
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Draft of approaches * Fixes * Make uuid unique * Apply suggestions from code review * Update exercises/practice/minesweeper/.approaches/introduction.md --------- Co-authored-by: Ryan Hartlage <[email protected]>
- Loading branch information
1 parent
514545a
commit e9a5a55
Showing
6 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"introduction": { | ||
"authors": ["meatball133"], | ||
"contributors": [] | ||
}, | ||
"approaches": [ | ||
{ | ||
"uuid": "7d266679-f573-42a5-80fa-66a9d15f2364", | ||
"slug": "data-driven", | ||
"title": "Data Driven", | ||
"blurb": "Using a data driven approach to setup the search areas", | ||
"authors": [ | ||
"meatball133" | ||
] | ||
}, | ||
{ | ||
"uuid": "5ac94a13-3e20-4b2c-a7ad-4d1426192e06", | ||
"slug": "translate", | ||
"title": "Translate", | ||
"blurb": "Uses the `tr` method to translate certain parts of a `String`", | ||
"authors": ["meatball133", "ryanplusplus"] | ||
} | ||
] | ||
} |
51 changes: 51 additions & 0 deletions
51
exercises/practice/minesweeper/.approaches/data-driven/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Data Driven | ||
|
||
```crystal | ||
class Minesweeper | ||
OFFSETS = [ | ||
{-1, -1}, {0, -1}, {1, -1}, | ||
{-1, 0}, {1, 0}, | ||
{-1, 1}, {0, 1}, {1, 1}, | ||
] | ||
def initialize(@board : Array(String) ) | ||
end | ||
def annotate() | ||
nrows = @board.size() | ||
@board.map_with_index do |row, y| | ||
ncols = row.size() | ||
row.chars.map_with_index do |cell, x| | ||
if cell == '*' | ||
next '*' | ||
end | ||
sum = OFFSETS.count do |(dx, dy)| | ||
new_x = x + dx | ||
new_y = y + dy | ||
new_x >= 0 && new_x < ncols && new_y >= 0 && new_y < nrows && @board[new_y][new_x] == '*' | ||
end | ||
sum == 0 ? ' ' : sum.to_s | ||
end.join | ||
end | ||
end | ||
end | ||
``` | ||
|
||
Using a data-driven approach to create logic to solve Minesweeper makes the solution concise and flexible. | ||
If the rules change, it is as easy as to change them. | ||
The solution starts by creating an `Array` with the rules which are tuples with the x and y offset. | ||
Defining an `initialize` method takes an `Array(String)` as an argument and assigns it to an instance variable `@board`. | ||
|
||
The `annotate` method starts by defining the number of rows on the board. | ||
Then it maps over the `@board` array with the index `y` for each row. | ||
Then, it converts each row into an array of `Char` and maps over it with the index `x` for each cell. | ||
|
||
If a cell is a mine, it will be skipped and the mine will be returned. | ||
Otherwise, it will count the number of mines around the cell. | ||
It will do this by having the rules defined beforehand and then calculating the new x and y positions for each rule. | ||
It will then check if the new x and y positions are within the board's bounds and if the cell is mine. | ||
If it is, the count will be increased. | ||
|
||
Finally, it will check if the sum is 0. If it is, it will return a space; otherwise, it will return the sum as a string. | ||
Then, it will join the row and return to the board. |
5 changes: 5 additions & 0 deletions
5
exercises/practice/minesweeper/.approaches/data-driven/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
OFFSETS = [ | ||
{-1, -1}, {0, -1}, {1, -1}, | ||
{-1, 0}, {1, 0}, | ||
{-1, 1}, {0, 1}, {1, 1}, | ||
] |
81 changes: 81 additions & 0 deletions
81
exercises/practice/minesweeper/.approaches/introduction.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# Introduction | ||
|
||
There are various ways to solve **minesweeper**. | ||
|
||
## Approach: Using translation | ||
|
||
Using the `tr` method to translate the result of the `annotated_space` method. | ||
This solution is very concise since it doesn't need any conditionals. | ||
It does this by using looping and the `tr` method. | ||
|
||
```crystal | ||
class Minesweeper | ||
def initialize(@board : Array(String)) | ||
end | ||
def annotate | ||
@board.map_with_index do |row, y| | ||
row.chars.map_with_index do |cell, x| | ||
cell.to_s.tr(" ", annotated_space(x, y)) | ||
end.join | ||
end | ||
end | ||
private def annotated_space(x, y) | ||
([y - 1, 0].max..[y + 1, @board.size - 1].min).map do |y| | ||
([x - 1, 0].max..[x + 1, @board[0].size - 1].min).count do |x| | ||
@board[y][x] == '*' | ||
end | ||
end.sum.to_s.tr("0", " ") | ||
end | ||
end | ||
``` | ||
|
||
For more information, see the [translate approach][approach-translate], or if you want to hear Erik Schierboom's thoughts on this approach, see the [video][video-translate]. | ||
|
||
## Approach: Using a data-driven approach | ||
|
||
Using rules to create logic to check for mines. | ||
This solution is concise and flexible. If the rules change, it is as easy as changing the rules. | ||
|
||
```crystal | ||
class Minesweeper | ||
OFFSETS = [ | ||
{-1, -1}, {0, -1}, {1, -1}, | ||
{-1, 0}, {1, 0}, | ||
{-1, 1}, {0, 1}, {1, 1}, | ||
] | ||
def initialize(@board : Array(String) ) | ||
end | ||
def annotate() | ||
nrows = @board.size() | ||
@board.map_with_index do |row, y| | ||
ncols = row.size() | ||
row.chars.map_with_index do |cell, x| | ||
if cell == '*' | ||
next '*' | ||
end | ||
sum = 0 | ||
OFFSETS.each do |(dx, dy)| | ||
new_x = x + dx | ||
new_y = y + dy | ||
if new_x < 0 || new_x > ncols - 1 || new_y < 0 || new_y > nrows - 1 || @board[new_y][new_x] == ' ' | ||
next | ||
end | ||
sum += 1 | ||
end | ||
sum == 0 ? ' ' : sum.to_s | ||
end.join | ||
end | ||
end | ||
end | ||
``` | ||
|
||
For more information, check the [data-driven approach][approach-data-driven]. | ||
|
||
[approach-data-driven]: https://exercism.org/tracks/crystal/exercises/minesweeper/approaches/data-driven | ||
[approach-translate]: https://exercism.org/tracks/crystal/exercises/minesweeper/approaches/translate | ||
[video-translate]: https://youtu.be/dLT2h2hODhs?t=951 |
52 changes: 52 additions & 0 deletions
52
exercises/practice/minesweeper/.approaches/translate/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Iteration and `tr` method | ||
|
||
```crystal | ||
class Minesweeper | ||
def initialize(@board : Array(String)) | ||
end | ||
def annotate | ||
@board.map_with_index do |row, y| | ||
row.chars.map_with_index do |cell, x| | ||
cell.to_s.tr(" ", annotated_space(x, y)) | ||
end.join | ||
end | ||
end | ||
private def annotated_space(x, y) | ||
([y - 1, 0].max..[y + 1, @board.size - 1].min).sum do |y| | ||
([x - 1, 0].max..[x + 1, @board[0].size - 1].min).count do |x| | ||
@board[y][x] == '*' | ||
end | ||
end.to_s.tr("0", " ") | ||
end | ||
end | ||
``` | ||
|
||
What makes this approach interesting is the use of the [`tr` method][tr-method], which allows us to have a concise solution that doesn't use conditionals. | ||
This approach starts with defining a `initialize` method that takes an `Array(String)` as an argument and assigns it to an instance variable `@board`. | ||
The `annotate` method starts with mapping over the `@board` array with the index `y` for each row. | ||
Then, it converts each row into an array of characters and maps over it with the index `x` for each cell. | ||
It converts each cell from a `Char` to a `String` and then uses the method `tr`. | ||
|
||
The `tr` method allows you to replace a set of characters with another set of characters in a string. | ||
This is useful since it allows us to replace only the space character with the result of the `annotated_space` method. | ||
|
||
The `annotated_space` method takes the `x` and `y` coordinates of the cell and calculates the number of mines around it. | ||
It then creates a range representing the area's height to check for mines. | ||
Since the index is 0, taking minus 1 would make the value -1, which would be valid in Crystal but would represent the last element. | ||
We don't want this behavior, so we use the [`max`][max-method] method to ensure that the value is at least 0. | ||
The same is done for the [`min`][min-method] value, but here we want to ensure that the value is, at most, the last index. | ||
|
||
Then, we use the [`sum`][sum-method] method to sum the total number of mines returned by the inner loop. | ||
The inner loop creates a range representing the area's width to check for mines. | ||
As before, we use the `max` and `min` methods to ensure that the values are within the board's bounds. | ||
Then, we use the [`count`][count-method] method to count how many times the inner condition is true, which indicates whether the cell is a mine or not. | ||
|
||
Finally, we convert the sum to a string and use the `tr` method to replace all the zeros with spaces. | ||
|
||
[tr-method]: https://crystal-lang.org/api/String.html#tr%28from%3AString%2Cto%3AString%29%3AString-instance-method | ||
[sum-method]: https://crystal-lang.org/api/Enumerable.html#sum-instance-method | ||
[count-method]: https://crystal-lang.org/api/Enumerable.html#count%28%26%3AT-%3E%29%3AInt32-instance-method | ||
[max-method]: https://crystal-lang.org/api/Enumerable.html#max%3AT-instance-method | ||
[min-method]: https://crystal-lang.org/api/Enumerable.html#min%3AT-instance-method |
7 changes: 7 additions & 0 deletions
7
exercises/practice/minesweeper/.approaches/translate/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
def annotate | ||
@board.map_with_index do |row, y| | ||
row.chars.map_with_index do |cell, x| | ||
cell.to_s.tr(" ", annotated_space(x, y)) | ||
end.join | ||
end | ||
end |