diff --git a/bin/generate-spec b/bin/generate-spec index fbe042d..aa7f5a4 100755 --- a/bin/generate-spec +++ b/bin/generate-spec @@ -85,11 +85,12 @@ if not path.exists(exercise_directory) if not path.exists("#{exercise_directory}/.meta/spec_generator.moon") die "#{exercise_name} does not have a spec_generator module.\n" -canonical_data_url = "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/#{exercise_name}/canonical-data.json" canonical_data_path = "canonical-data/#{exercise_name}.json" -assert os.execute('mkdir -p "$(dirname "' .. canonical_data_path .. '")"') -assert os.execute('curl "' .. canonical_data_url .. '" -s -o "' .. canonical_data_path .. '"') +if not path.exists canonical_data_path + canonical_data_url = "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/#{exercise_name}/canonical-data.json" + assert os.execute('mkdir -p "$(dirname "' .. canonical_data_path .. '")"') + assert os.execute('curl "' .. canonical_data_url .. '" -s -o "' .. canonical_data_path .. '"') -- "json.null" ref: https://dkolf.de/dkjson-lua/documentation canonical_data = json.decode file.read(canonical_data_path, false), 1, json.null diff --git a/config.json b/config.json index 1110977..a269902 100644 --- a/config.json +++ b/config.json @@ -578,6 +578,14 @@ "prerequisites": [], "difficulty": 4 }, + { + "slug": "save-the-cow", + "name": "Save the Cow", + "uuid": "7b85dc03-54e5-45ea-ac02-a9da503c89e9", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "series", "name": "Series", diff --git a/exercises/practice/save-the-cow/.busted b/exercises/practice/save-the-cow/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/save-the-cow/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/save-the-cow/.docs/instructions.md b/exercises/practice/save-the-cow/.docs/instructions.md new file mode 100644 index 0000000..1ec98a4 --- /dev/null +++ b/exercises/practice/save-the-cow/.docs/instructions.md @@ -0,0 +1,7 @@ +# Instructions + +Implement the logic for a word-guessing game. + +A player tries to solve a secret word by guessing individual letters. +They win if they reveal all the letters in the secret word. +They lose if they make ten incorrect guesses before revealing the word. diff --git a/exercises/practice/save-the-cow/.docs/introduction.md b/exercises/practice/save-the-cow/.docs/introduction.md new file mode 100644 index 0000000..c87a7fa --- /dev/null +++ b/exercises/practice/save-the-cow/.docs/introduction.md @@ -0,0 +1,4 @@ +# Introduction + +Bessie the cow has wandered onto an alien spaceship. +Guess the secret door code to bring her home before the ship blasts off. diff --git a/exercises/practice/save-the-cow/.meta/config.json b/exercises/practice/save-the-cow/.meta/config.json new file mode 100644 index 0000000..4af182c --- /dev/null +++ b/exercises/practice/save-the-cow/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "save_the_cow.moon" + ], + "test": [ + "save_the_cow_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Implement a word-guessing game." +} diff --git a/exercises/practice/save-the-cow/.meta/example.moon b/exercises/practice/save-the-cow/.meta/example.moon new file mode 100644 index 0000000..8ec82c3 --- /dev/null +++ b/exercises/practice/save-the-cow/.meta/example.moon @@ -0,0 +1,35 @@ +class SaveTheCow + new: (@word) => + @guessed = {} + @errsAvailable = 9 + @state = 'Ongoing' + + guess: (letters) => + for guess in *letters + if @state == 'Win' then error 'cannot guess after the game is won' + if @state == 'Lose' then error 'cannot guess after the game is lost' + + if @guessed[guess] + @errsAvailable -= 1 + else + found = not not @word\find guess + @guessed[guess] = found + if not found + @errsAvailable -= 1 + + @state = 'Lose' if @errsAvailable < 0 + @state = 'Win' if not @mask!\find '_' + + @currentState! + + currentState: => + { + state: @state, + maskedWord: @mask!, + remainingFailures: math.max 0, @errsAvailable + } + + mask: => + @word\gsub '%a', (c) -> @guessed[c] and c or '_' + + diff --git a/exercises/practice/save-the-cow/.meta/spec_generator.moon b/exercises/practice/save-the-cow/.meta/spec_generator.moon new file mode 100644 index 0000000..f641c4e --- /dev/null +++ b/exercises/practice/save-the-cow/.meta/spec_generator.moon @@ -0,0 +1,21 @@ +import indent, quote, table_dump, word_list from require 'spec_helpers' + +{ + module_name: 'SaveTheCow', + + generate_test: (case, level) -> + lines = if case.expected.error + { + "game = SaveTheCow #{quote case.input.word}", + "f = -> game\\guess #{word_list case.input.guesses}", + "assert.has.error f, #{quote case.expected.error}" + } + else + { + "game = SaveTheCow #{quote case.input.word}", + "result = game\\guess #{word_list case.input.guesses}", + "expected = #{table_dump case.expected, level}", + "assert.are.same expected, result" + } + table.concat [indent line, level for line in *lines], '\n' +} diff --git a/exercises/practice/save-the-cow/.meta/tests.toml b/exercises/practice/save-the-cow/.meta/tests.toml new file mode 100644 index 0000000..eca72c4 --- /dev/null +++ b/exercises/practice/save-the-cow/.meta/tests.toml @@ -0,0 +1,40 @@ +# 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. + +[71d340f9-fc29-4826-872e-ad7d0b83dd98] +description = "Initially 9 failures are allowed and no letters are guessed" + +[76759c24-8f1a-4fc8-9ffd-d6a1ff0cf03b] +description = "After 10 failures the game is over" + +[d6f2e202-7857-46fb-b709-43a7f3f3b2de] +description = "Losing with several correct guesses" + +[71bc0cda-2032-4637-80c8-fc8771124c08] +description = "Feeding a correct letter removes underscores" + +[5b568a1c-867d-418f-97a8-7b6f8a7ca0a2] +description = "Feeding a correct letter twice counts as a failure" + +[3d40f15b-0271-4c5d-b1a4-3e1f66ff221f] +description = "Guessing a repeated letter reveals all instances" + +[11a86435-e401-4250-a26e-3b0d8c4049ad] +description = "Getting all the letters right makes for a win" + +[b3d81876-84ee-45bb-b531-baa1b05b4709] +description = "Winning on the last guess is still a win" + +[cf204398-5e9f-402f-8bff-42cb7cbbbda9] +description = "Guessing after a lose is error" + +[c2ec5b3d-4923-4a0e-a485-6aa6e78c7ece] +description = "Guessing after a win is error" diff --git a/exercises/practice/save-the-cow/save_the_cow.moon b/exercises/practice/save-the-cow/save_the_cow.moon new file mode 100644 index 0000000..b8b3000 --- /dev/null +++ b/exercises/practice/save-the-cow/save_the_cow.moon @@ -0,0 +1,6 @@ +class SaveTheCow + new: (word) => + error 'Implement the constructor' + + guess: (letters) => + error 'Implement the guess method' diff --git a/exercises/practice/save-the-cow/save_the_cow_spec.moon b/exercises/practice/save-the-cow/save_the_cow_spec.moon new file mode 100644 index 0000000..0ca3d04 --- /dev/null +++ b/exercises/practice/save-the-cow/save_the_cow_spec.moon @@ -0,0 +1,92 @@ +SaveTheCow = require 'save_the_cow' + +describe 'save-the-cow:', -> + it 'Initially 9 failures are allowed and no letters are guessed', -> + game = SaveTheCow 'loot' + result = game\guess {} + expected = { + remainingFailures: 9 + state: 'Ongoing' + maskedWord: '____' + } + assert.are.same expected, result + + pending 'After 10 failures the game is over', -> + game = SaveTheCow 'loot' + result = game\guess {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} + expected = { + remainingFailures: 0 + state: 'Lose' + maskedWord: '____' + } + assert.are.same expected, result + + pending 'Losing with several correct guesses', -> + game = SaveTheCow 'loot' + result = game\guess {'t', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} + expected = { + remainingFailures: 0 + state: 'Lose' + maskedWord: '_oot' + } + assert.are.same expected, result + + pending 'Feeding a correct letter removes underscores', -> + game = SaveTheCow 'loot' + result = game\guess {'t'} + expected = { + remainingFailures: 9 + state: 'Ongoing' + maskedWord: '___t' + } + assert.are.same expected, result + + pending 'Feeding a correct letter twice counts as a failure', -> + game = SaveTheCow 'loot' + result = game\guess {'t', 't'} + expected = { + remainingFailures: 8 + state: 'Ongoing' + maskedWord: '___t' + } + assert.are.same expected, result + + pending 'Guessing a repeated letter reveals all instances', -> + game = SaveTheCow 'loot' + result = game\guess {'t', 't', 'o'} + expected = { + remainingFailures: 8 + state: 'Ongoing' + maskedWord: '_oot' + } + assert.are.same expected, result + + pending 'Getting all the letters right makes for a win', -> + game = SaveTheCow 'loot' + result = game\guess {'t', 't', 'o', 'l'} + expected = { + remainingFailures: 8 + state: 'Win' + maskedWord: 'loot' + } + assert.are.same expected, result + + pending 'Winning on the last guess is still a win', -> + game = SaveTheCow 'loot' + result = game\guess {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 't', 'o', 'l'} + expected = { + remainingFailures: 0 + state: 'Win' + maskedWord: 'loot' + } + assert.are.same expected, result + + pending 'Guessing after a lose is error', -> + game = SaveTheCow 'loot' + f = -> game\guess {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'} + assert.has.error f, 'cannot guess after the game is lost' + + pending 'Guessing after a win is error', -> + game = SaveTheCow 'loot' + f = -> game\guess {'t', 'o', 'l', 'l'} + assert.has.error f, 'cannot guess after the game is won'