Skip to content

Commit b3cf79f

Browse files
authored
[Flower-Field]: Reimplement Minesweeper as Flower Field (exercism#3934)
* Reimplemented Minesweeper as Flower Field and regenerated test cases. * Removed accidental solution from stub file.
1 parent b7a33e7 commit b3cf79f

File tree

11 files changed

+330
-0
lines changed

11 files changed

+330
-0
lines changed

config.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,22 @@
14121412
],
14131413
"difficulty": 4
14141414
},
1415+
{
1416+
"slug": "flower-field",
1417+
"name": "Flower Field",
1418+
"uuid": "0c2751c1-5d2f-499a-81b8-226e5092ea88",
1419+
"practices": ["lists"],
1420+
"prerequisites": [
1421+
"conditionals",
1422+
"lists",
1423+
"list-methods",
1424+
"loops",
1425+
"numbers",
1426+
"strings",
1427+
"string-methods"
1428+
],
1429+
"difficulty": 4
1430+
},
14151431
{
14161432
"slug": "rail-fence-cipher",
14171433
"name": "Rail Fence Cipher",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Instructions append
2+
3+
## Exception messages
4+
5+
Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.
6+
7+
This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when the `board()` function receives malformed input. The tests will only pass if you both `raise` the `exception` and include a message with it.
8+
9+
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
10+
11+
```python
12+
# when the board receives malformed input
13+
raise ValueError("The board is invalid with current input.")
14+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Instructions
2+
3+
Your task is to add flower counts to empty squares in a completed Flower Field garden.
4+
The garden itself is a rectangle board composed of squares that are either empty (`' '`) or a flower (`'*'`).
5+
6+
For each empty square, count the number of flowers adjacent to it (horizontally, vertically, diagonally).
7+
If the empty square has no adjacent flowers, leave it empty.
8+
Otherwise replace it with the count of adjacent flowers.
9+
10+
For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen):
11+
12+
```text
13+
·*·*·
14+
··*··
15+
··*··
16+
·····
17+
```
18+
19+
Which your code should transform into this:
20+
21+
```text
22+
1*3*1
23+
13*31
24+
·2*2·
25+
·111·
26+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Introduction
2+
3+
[Flower Field][history] is a compassionate reimagining of the popular game Minesweeper.
4+
The object of the game is to find all the flowers in the garden using numeric hints that indicate how many flowers are directly adjacent (horizontally, vertically, diagonally) to a square.
5+
"Flower Field" shipped in regional versions of Microsoft Windows in Italy, Germany, South Korea, Japan and Taiwan.
6+
7+
[history]: https://web.archive.org/web/20020409051321fw_/http://rcm.usr.dsi.unimi.it/rcmweb/fnm/
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"exercise": "flower-field",
3+
"version": "2.0",
4+
"comments": [
5+
" The expected outputs are represented as arrays of strings to ",
6+
" improve readability in this JSON file. ",
7+
" Your track may choose whether to present the input as a single ",
8+
" string (concatenating all the lines) or as the list. "
9+
],
10+
"cases": [
11+
{
12+
"description": "annotate 9",
13+
"property": "annotate",
14+
"input": {
15+
"garden": [
16+
" ",
17+
" * ",
18+
" ",
19+
" ",
20+
" * "
21+
]
22+
},
23+
"expected": [
24+
" 111",
25+
" 1*1",
26+
" 111",
27+
"111 ",
28+
"1*1 "
29+
]
30+
},
31+
{
32+
"description": "different len",
33+
"property": "annotate",
34+
"input": {
35+
"garden": [
36+
" ",
37+
"* ",
38+
" "
39+
]
40+
},
41+
"expected": {"error": "The board is invalid with current input."}
42+
},
43+
{
44+
"description": "invalid char",
45+
"property": "annotate",
46+
"input": {
47+
"garden": ["X * "]
48+
},
49+
"expected": {"error": "The board is invalid with current input."}
50+
51+
}
52+
]
53+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"authors": [
3+
"habere-et-dispertire",
4+
"bethanyg"
5+
],
6+
"contributors": [
7+
"isaacg",
8+
"kotp"
9+
],
10+
"files": {
11+
"solution": [
12+
"flower_field.py"
13+
],
14+
"test": [
15+
"flower_field_test.py"
16+
],
17+
"example": [
18+
".meta/example.py"
19+
]
20+
},
21+
"blurb": "Mark all the flowers in a garden."
22+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
def annotate(garden):
2+
if not garden:
3+
return []
4+
verify_board(garden)
5+
row_len = len(garden[0])
6+
col_len = len(garden)
7+
board = [list(row) for row in garden]
8+
9+
for index1 in range(col_len):
10+
for index2 in range(row_len):
11+
if board[index1][index2] != ' ':
12+
continue
13+
14+
low = max(index2 - 1, 0)
15+
high = min(index2 + 2, row_len + 2)
16+
counts = garden[index1][low:high].count('*')
17+
18+
if index1 > 0:
19+
counts += garden[index1 - 1][low:high].count('*')
20+
if index1 < col_len - 1:
21+
counts += garden[index1 + 1][low:high].count('*')
22+
if counts == 0:
23+
continue
24+
25+
board[index1][index2] = str(counts)
26+
return [''.join(row) for row in board]
27+
28+
29+
def verify_board(garden):
30+
# Rows with different lengths
31+
row_len = len(garden[0])
32+
if not all(len(row) == row_len for row in garden):
33+
raise ValueError('The board is invalid with current input.')
34+
35+
# Unknown character in board
36+
character_set = set()
37+
for row in garden:
38+
character_set.update(row)
39+
if character_set - set(' *'):
40+
raise ValueError('The board is invalid with current input.')
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{%- import "generator_macros.j2" as macros with context -%}
2+
{{ macros.canonical_ref() }}
3+
4+
{{ macros.header()}}
5+
6+
{%- macro test_call(case) -%}
7+
{{ case["property"] | to_snake }}({{ case["input"]["garden"] }})
8+
{%- endmacro %}
9+
10+
class {{ exercise | camel_case }}Test(unittest.TestCase):
11+
{% for case in cases -%}
12+
def test_{{ case["description"] | to_snake }}(self):
13+
self.assertEqual({{ test_call(case) }}, {{ case["expected"] }})
14+
{% endfor %}
15+
16+
# Additional tests for this track
17+
{% for case in additional_cases -%}
18+
def test_{{ case["description"] | to_snake }}(self):
19+
{%- if case is error_case %}
20+
with self.assertRaises(ValueError) as err:
21+
{{ test_call(case) }}
22+
self.assertEqual(type(err.exception), ValueError)
23+
self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
24+
{%- else %}
25+
self.assertEqual({{- test_call(case) }}, {{ case["expected"] }})
26+
{%- endif %}
27+
{% endfor %}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[237ff487-467a-47e1-9b01-8a891844f86c]
13+
description = "no rows"
14+
15+
[4b4134ec-e20f-439c-a295-664c38950ba1]
16+
description = "no columns"
17+
18+
[d774d054-bbad-4867-88ae-069cbd1c4f92]
19+
description = "no flowers"
20+
21+
[225176a0-725e-43cd-aa13-9dced501f16e]
22+
description = "garden full of flowers"
23+
24+
[3f345495-f1a5-4132-8411-74bd7ca08c49]
25+
description = "flower surrounded by spaces"
26+
27+
[6cb04070-4199-4ef7-a6fa-92f68c660fca]
28+
description = "space surrounded by flowers"
29+
30+
[272d2306-9f62-44fe-8ab5-6b0f43a26338]
31+
description = "horizontal line"
32+
33+
[c6f0a4b2-58d0-4bf6-ad8d-ccf4144f1f8e]
34+
description = "horizontal line, flowers at edges"
35+
36+
[a54e84b7-3b25-44a8-b8cf-1753c8bb4cf5]
37+
description = "vertical line"
38+
39+
[b40f42f5-dec5-4abc-b167-3f08195189c1]
40+
description = "vertical line, flowers at edges"
41+
42+
[58674965-7b42-4818-b930-0215062d543c]
43+
description = "cross"
44+
45+
[dd9d4ca8-9e68-4f78-a677-a2a70fd7a7b8]
46+
description = "large garden"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def annotate(garden):
2+
# Function body starts here
3+
pass

0 commit comments

Comments
 (0)