From 0881024fa66ca1ef888c5d8a9416bc159c39d83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Sun, 29 Jun 2025 09:06:53 -0700 Subject: [PATCH] Add `flower-field`, deprecating `minesweeper` --- config.json | 15 +- .../flower-field/.docs/instructions.md | 26 +++ .../flower-field/.docs/introduction.md | 7 + .../practice/flower-field/.meta/config.json | 23 +++ .../practice/flower-field/.meta/example.erl | 73 ++++++++ .../practice/flower-field/.meta/tests.toml | 46 +++++ exercises/practice/flower-field/rebar.config | 30 +++ .../flower-field/src/flower_field.app.src | 9 + .../flower-field/src/flower_field.erl | 6 + .../flower-field/test/flower_field_tests.erl | 177 ++++++++++++++++++ 10 files changed, 409 insertions(+), 3 deletions(-) create mode 100644 exercises/practice/flower-field/.docs/instructions.md create mode 100644 exercises/practice/flower-field/.docs/introduction.md create mode 100644 exercises/practice/flower-field/.meta/config.json create mode 100644 exercises/practice/flower-field/.meta/example.erl create mode 100644 exercises/practice/flower-field/.meta/tests.toml create mode 100644 exercises/practice/flower-field/rebar.config create mode 100644 exercises/practice/flower-field/src/flower_field.app.src create mode 100644 exercises/practice/flower-field/src/flower_field.erl create mode 100644 exercises/practice/flower-field/test/flower_field_tests.erl diff --git a/config.json b/config.json index 3175db29..963542ac 100644 --- a/config.json +++ b/config.json @@ -407,9 +407,9 @@ ] }, { - "slug": "minesweeper", - "name": "Minesweeper", - "uuid": "2cf5d5ba-9820-4918-ac91-811b2a434655", + "slug": "flower-field", + "name": "Flower Field", + "uuid": "a9557d94-5839-489f-b357-a28d1f1fba2e", "practices": [], "prerequisites": [], "difficulty": 7, @@ -420,6 +420,15 @@ "transforming" ] }, + { + "slug": "minesweeper", + "name": "Minesweeper", + "uuid": "2cf5d5ba-9820-4918-ac91-811b2a434655", + "practices": [], + "prerequisites": [], + "difficulty": 7, + "status": "deprecated" + }, { "slug": "dominoes", "name": "Dominoes", diff --git a/exercises/practice/flower-field/.docs/instructions.md b/exercises/practice/flower-field/.docs/instructions.md new file mode 100644 index 00000000..bbdae0c2 --- /dev/null +++ b/exercises/practice/flower-field/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to add flower counts to empty squares in a completed Flower Field garden. +The garden itself is a rectangle board composed of squares that are either empty (`' '`) or a flower (`'*'`). + +For each empty square, count the number of flowers adjacent to it (horizontally, vertically, diagonally). +If the empty square has no adjacent flowers, leave it empty. +Otherwise replace it with the count of adjacent flowers. + +For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen): + +```text +·*·*· +··*·· +··*·· +····· +``` + +Which your code should transform into this: + +```text +1*3*1 +13*31 +·2*2· +·111· +``` diff --git a/exercises/practice/flower-field/.docs/introduction.md b/exercises/practice/flower-field/.docs/introduction.md new file mode 100644 index 00000000..af9b6153 --- /dev/null +++ b/exercises/practice/flower-field/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +[Flower Field][history] is a compassionate reimagining of the popular game Minesweeper. +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. +"Flower Field" shipped in regional versions of Microsoft Windows in Italy, Germany, South Korea, Japan and Taiwan. + +[history]: https://web.archive.org/web/20020409051321fw_/http://rcm.usr.dsi.unimi.it/rcmweb/fnm/ diff --git a/exercises/practice/flower-field/.meta/config.json b/exercises/practice/flower-field/.meta/config.json new file mode 100644 index 00000000..3c7b5746 --- /dev/null +++ b/exercises/practice/flower-field/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "juhlig" + ], + "contributors": [ + "BNAndras", + "ErikSchierboom", + "iHiD", + "NobbZ" + ], + "files": { + "solution": [ + "src/flower_field.erl" + ], + "test": [ + "test/flower_field_tests.erl" + ], + "example": [ + ".meta/example.erl" + ] + }, + "blurb": "Mark all the flowers in a garden." +} diff --git a/exercises/practice/flower-field/.meta/example.erl b/exercises/practice/flower-field/.meta/example.erl new file mode 100644 index 00000000..d2c04b95 --- /dev/null +++ b/exercises/practice/flower-field/.meta/example.erl @@ -0,0 +1,73 @@ +-module(example). + +-export([annotate/1]). + +%% no rows +annotate([]) -> + []; + +%% no columns +annotate(Garden=[""|_]) -> + Garden; + +%% proper garden +annotate(Garden) -> + process_garden(extend(Garden)). + + +%% surround the actual garden with non-flowers +%% the rows and columns of the extended garden will be implicitly reversed +extend(Garden=[R|_]) -> + EdgeRow=[16#20 || _ <- lists:seq(1, length(R)+2)], + [EdgeRow|extend(Garden, [EdgeRow])]. + +extend([], Acc) -> + Acc; + +extend([Row|More], Acc) -> + ExtendedRow=[16#20|lists:reverse([16#20|Row])], + extend(More, [ExtendedRow|Acc]). + + +%% process the (extended) garden +%% the rows and columns of the processed garden will be implicitly reversed, +%% thereby restoring the original ordering +%% three rows need to be considered: the previous, the current, and the next +process_garden(Garden) -> + process_garden(Garden, []). + +%% only two rows left, at end of garden +process_garden([_, _], Acc) -> + Acc; + +%% at least 3 rows left, process them +process_garden([PrevRow|More=[CurRow, NextRow|_]], Acc) -> + process_garden(More, [process_row(PrevRow, CurRow, NextRow)|Acc]). + + +%% process a row of the garden +%% three columns of the previous and next rows and two of the current row need to be considered +process_row(PrevRow, CurRow, NextRow) -> + process_row(PrevRow, CurRow, NextRow, []). + +%% only two columns left, at end of row +process_row([_, _], [_, _], [_, _], Acc) -> + Acc; + +%% current element is a flower, don't change it +process_row([_|MorePrev], [_|MoreCur=[$*|_]], [_|MoreNext], Acc) -> + process_row(MorePrev, MoreCur, MoreNext, [$*|Acc]); + +%% current element is not a flower, count surrounding flowers, translate to character +process_row([PrevPrev|MorePrev=[PrevCur, PrevNext|_]], [CurPrev|MoreCur=[_, CurNext|_]], [NextPrev|MoreNext=[NextCur, NextNext|_]], Acc) -> + N=lists:foldl( + fun ($*, Count) -> Count+1; (16#20, Count) -> Count end, + 0, + [PrevPrev, PrevCur, PrevNext, CurPrev, CurNext, NextPrev, NextCur, NextNext] + ), + process_row(MorePrev, MoreCur, MoreNext, [count_to_char(N)|Acc]). + + +%% translate a count into a character +count_to_char(0) -> 16#20; +count_to_char(N) -> $0+N. diff --git a/exercises/practice/flower-field/.meta/tests.toml b/exercises/practice/flower-field/.meta/tests.toml new file mode 100644 index 00000000..c2b24fda --- /dev/null +++ b/exercises/practice/flower-field/.meta/tests.toml @@ -0,0 +1,46 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[237ff487-467a-47e1-9b01-8a891844f86c] +description = "no rows" + +[4b4134ec-e20f-439c-a295-664c38950ba1] +description = "no columns" + +[d774d054-bbad-4867-88ae-069cbd1c4f92] +description = "no flowers" + +[225176a0-725e-43cd-aa13-9dced501f16e] +description = "garden full of flowers" + +[3f345495-f1a5-4132-8411-74bd7ca08c49] +description = "flower surrounded by spaces" + +[6cb04070-4199-4ef7-a6fa-92f68c660fca] +description = "space surrounded by flowers" + +[272d2306-9f62-44fe-8ab5-6b0f43a26338] +description = "horizontal line" + +[c6f0a4b2-58d0-4bf6-ad8d-ccf4144f1f8e] +description = "horizontal line, flowers at edges" + +[a54e84b7-3b25-44a8-b8cf-1753c8bb4cf5] +description = "vertical line" + +[b40f42f5-dec5-4abc-b167-3f08195189c1] +description = "vertical line, flowers at edges" + +[58674965-7b42-4818-b930-0215062d543c] +description = "cross" + +[dd9d4ca8-9e68-4f78-a677-a2a70fd7a7b8] +description = "large garden" diff --git a/exercises/practice/flower-field/rebar.config b/exercises/practice/flower-field/rebar.config new file mode 100644 index 00000000..db5d9076 --- /dev/null +++ b/exercises/practice/flower-field/rebar.config @@ -0,0 +1,30 @@ +%% Erlang compiler options +{erl_opts, [debug_info, warnings_as_errors]}. + +{deps, [{erl_exercism, "0.1.2"}]}. + +{dialyzer, [ + {warnings, [underspecs, no_return]}, + {get_warnings, true}, + {plt_apps, top_level_deps}, % top_level_deps | all_deps + {plt_extra_apps, []}, + {plt_location, local}, % local | "/my/file/name" + {plt_prefix, "rebar3"}, + {base_plt_apps, [stdlib, kernel, crypto]}, + {base_plt_location, global}, % global | "/my/file/name" + {base_plt_prefix, "rebar3"} +]}. + +%% eunit:test(Tests) +{eunit_tests, []}. +%% Options for eunit:test(Tests, Opts) +{eunit_opts, [verbose]}. + +%% == xref == + +{xref_warnings, true}. + +%% xref checks to run +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, exports_not_used, + deprecated_function_calls, deprecated_functions]}. diff --git a/exercises/practice/flower-field/src/flower_field.app.src b/exercises/practice/flower-field/src/flower_field.app.src new file mode 100644 index 00000000..6130f658 --- /dev/null +++ b/exercises/practice/flower-field/src/flower_field.app.src @@ -0,0 +1,9 @@ +{application, flower_field, + [{description, "exercism.org - flower-field"}, + {vsn, "0.0.1"}, + {modules, []}, + {registered, []}, + {applications, [kernel, + stdlib]}, + {env, []} + ]}. diff --git a/exercises/practice/flower-field/src/flower_field.erl b/exercises/practice/flower-field/src/flower_field.erl new file mode 100644 index 00000000..0bfe782f --- /dev/null +++ b/exercises/practice/flower-field/src/flower_field.erl @@ -0,0 +1,6 @@ +-module(flower_field). + +-export([annotate/1]). + + +annotate(_Garden) -> undefined. diff --git a/exercises/practice/flower-field/test/flower_field_tests.erl b/exercises/practice/flower-field/test/flower_field_tests.erl new file mode 100644 index 00000000..95a994ea --- /dev/null +++ b/exercises/practice/flower-field/test/flower_field_tests.erl @@ -0,0 +1,177 @@ +-module(flower_field_tests). + +-include_lib("erl_exercism/include/exercism.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + + + +'1_no_rows_test_'() -> + Input=[ + + ], + Expected=[ + + ], + {"no rows", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'2_no_columns_test_'() -> + Input=[ + "" + ], + Expected=[ + "" + ], + {"no columns", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'3_no_flowers_test_'() -> + Input=[ + " ", + " ", + " " + ], + Expected=[ + " ", + " ", + " " + ], + {"no flowers", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'4_garden_full_of_flowers_test_'() -> + Input=[ + "***", + "***", + "***" + ], + Expected=[ + "***", + "***", + "***" + ], + {"flowerfield with only flowers", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'5_flower_surrounded_by_spaces_test_'() -> + Input=[ + " ", + " * ", + " " + ], + Expected=[ + "111", + "1*1", + "111" + ], + {"flower surrounded by spaces", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'6_space_surrounded_by_flowers_test_'() -> + Input=[ + "***", + "* *", + "***" + ], + Expected=[ + "***", + "*8*", + "***" + ], + {"space surrounded by flowers", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'7_horizontal_line_test_'() -> + Input=[ + " * * " + ], + Expected=[ + "1*2*1" + ], + {"horizontal line", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'8_horizontal_line_flowers_at_edges_test_'() -> + Input=[ + "* *" + ], + Expected=[ + "*1 1*" + ], + {"horizontal line, flowers at edges", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'9_vertical_line_test_'() -> + Input=[ + " ", + "*", + " ", + "*", + " " + ], + Expected=[ + "1", + "*", + "2", + "*", + "1" + ], + {"vertical line", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'10_vertical_line_flowers_at_edges_test_'() -> + Input=[ + "*", + " ", + " ", + " ", + "*" + ], + Expected=[ + "*", + "1", + " ", + "1", + "*" + ], + {"vertical line, flowers at edges", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'11_cross_test_'() -> + Input=[ + " * ", + " * ", + "*****", + " * ", + " * " + ], + Expected=[ + " 2*2 ", + "25*52", + "*****", + "25*52", + " 2*2 " + ], + {"cross", + ?_assertMatch(Expected, flower_field:annotate(Input))}. + +'12_large_flowerfield_test_'() -> + Input=[ + " * * ", + " * ", + " * ", + " * *", + " * * ", + " " + ], + Expected=[ + "1*22*1", + "12*322", + " 123*2", + "112*4*", + "1*22*2", + "111111" + ], + {"large garden", + ?_assertMatch(Expected, flower_field:annotate(Input))}.