diff --git a/config.json b/config.json index 7e177bd..b3ad6ab 100644 --- a/config.json +++ b/config.json @@ -906,6 +906,14 @@ "prerequisites": [], "difficulty": 8 }, + { + "slug": "connect", + "name": "Connect", + "uuid": "8c824d1a-0609-4c72-8a3b-512a8469cc8f", + "practices": [], + "prerequisites": [], + "difficulty": 8 + }, { "slug": "forth", "name": "Forth", diff --git a/exercises/practice/connect/.busted b/exercises/practice/connect/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/connect/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/connect/.docs/instructions.md b/exercises/practice/connect/.docs/instructions.md new file mode 100644 index 0000000..7f34bfa --- /dev/null +++ b/exercises/practice/connect/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Compute the result for a game of Hex / Polygon. + +The abstract boardgame known as [Hex][hex] / Polygon / CON-TAC-TIX is quite simple in rules, though complex in practice. +Two players place stones on a parallelogram with hexagonal fields. +The player to connect his/her stones to the opposite side first wins. +The four sides of the parallelogram are divided between the two players (i.e. one player gets assigned a side and the side directly opposite it and the other player gets assigned the two other sides). + +Your goal is to build a program that given a simple representation of a board computes the winner (or lack thereof). +Note that all games need not be "fair". +(For example, players may have mismatched piece counts or the game's board might have a different width and height.) + +The boards look like this: + +```text +. O . X . + . X X O . + O O O X . + . X O X O + X O O O X +``` + +"Player `O`" plays from top to bottom, "Player `X`" plays from left to right. +In the above example `O` has made a connection from left to right but nobody has won since `O` didn't connect top and bottom. + +[hex]: https://en.wikipedia.org/wiki/Hex_%28board_game%29 diff --git a/exercises/practice/connect/.meta/config.json b/exercises/practice/connect/.meta/config.json new file mode 100644 index 0000000..c939a68 --- /dev/null +++ b/exercises/practice/connect/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "connect.moon" + ], + "test": [ + "connect_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Compute the result for a game of Hex / Polygon." +} diff --git a/exercises/practice/connect/.meta/example.moon b/exercises/practice/connect/.meta/example.moon new file mode 100644 index 0000000..173d046 --- /dev/null +++ b/exercises/practice/connect/.meta/example.moon @@ -0,0 +1,38 @@ +-- --------------------------------------------------- +clone = (t) -> [ [c for c in *row] for row in *t] +transpose = (t) -> + [ [t[row][col] for row = 1, #t] for col = 1, #t[1]] + +-- --------------------------------------------------- +win = (player, mtx) -> + size = #mtx -- assume square board + + -- seed the stack with players on the first row + stack = [{1, i} for i = 1, size when mtx[1][i] == player] + return false if #stack == 0 + return true if size == 1 + + neighbours = (r, c) -> + ns = {} + for {dr, dc} in *{{-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}} + rr, cc = r + dr, c + dc + if rr >= 1 and rr <= size and cc >= 1 and cc <= size + table.insert ns, {rr, cc} if mtx[rr][cc] == player + ns + + while #stack > 0 + {r, c} = table.remove stack + for {rr, cc} in *neighbours r, c + return true if rr == size -- winner reached the last row + mtx[rr][cc] = '-' -- mark as visited + table.insert stack, {rr, cc} + false + +-- --------------------------------------------------- +{ + winner: (board) -> + matrix = [ [c for c in row\gmatch '[XO.]'] for row in *board] + return 'O' if win 'O', clone(matrix) -- top to bottom + return 'X' if win 'X', transpose(matrix) -- left to right + return '' +} diff --git a/exercises/practice/connect/.meta/spec_generator.moon b/exercises/practice/connect/.meta/spec_generator.moon new file mode 100644 index 0000000..c09df51 --- /dev/null +++ b/exercises/practice/connect/.meta/spec_generator.moon @@ -0,0 +1,13 @@ +import string_list from require 'test_helpers' + +{ + -- one of: + module_name: 'Connect', + + generate_test: (case, level) -> + lines = { + "board = #{string_list case.input.board, level}", + "assert.are.equal #{quote case.expected}, Connect.#{case.property} board" + } + table.concat [indent line, level for line in *lines], '\n' +} diff --git a/exercises/practice/connect/.meta/tests.toml b/exercises/practice/connect/.meta/tests.toml new file mode 100644 index 0000000..951b87e --- /dev/null +++ b/exercises/practice/connect/.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. + +[6eff0df4-3e92-478d-9b54-d3e8b354db56] +description = "an empty board has no winner" + +[298b94c0-b46d-45d8-b34b-0fa2ea71f0a4] +description = "X can win on a 1x1 board" + +[763bbae0-cb8f-4f28-bc21-5be16a5722dc] +description = "O can win on a 1x1 board" + +[819fde60-9ae2-485e-a024-cbb8ea68751b] +description = "only edges does not make a winner" + +[2c56a0d5-9528-41e5-b92b-499dfe08506c] +description = "illegal diagonal does not make a winner" + +[41cce3ef-43ca-4963-970a-c05d39aa1cc1] +description = "nobody wins crossing adjacent angles" + +[cd61c143-92f6-4a8d-84d9-cb2b359e226b] +description = "X wins crossing from left to right" + +[495e33ed-30a9-4012-b46e-d7c4d5fe13c3] +description = "X wins with left-hand dead end fork" + +[ab167ab0-4a98-4d0f-a1c0-e1cddddc3d58] +description = "X wins with right-hand dead end fork" + +[73d1eda6-16ab-4460-9904-b5f5dd401d0b] +description = "O wins crossing from top to bottom" + +[c3a2a550-944a-4637-8b3f-1e1bf1340a3d] +description = "X wins using a convoluted path" + +[17e76fa8-f731-4db7-92ad-ed2a285d31f3] +description = "X wins using a spiral path" diff --git a/exercises/practice/connect/connect.moon b/exercises/practice/connect/connect.moon new file mode 100644 index 0000000..a321b32 --- /dev/null +++ b/exercises/practice/connect/connect.moon @@ -0,0 +1,4 @@ +{ + winner: (board) -> + error 'Implement me' +} diff --git a/exercises/practice/connect/connect_spec.moon b/exercises/practice/connect/connect_spec.moon new file mode 100644 index 0000000..08c029b --- /dev/null +++ b/exercises/practice/connect/connect_spec.moon @@ -0,0 +1,111 @@ +Connect = require 'connect' + +describe 'connect', -> + it 'an empty board has no winner', -> + board = { + '. . . . .', + ' . . . . .', + ' . . . . .', + ' . . . . .', + ' . . . . .', + } + assert.are.equal '', Connect.winner board + + pending 'X can win on a 1x1 board', -> + board = {'X'} + assert.are.equal 'X', Connect.winner board + + pending 'O can win on a 1x1 board', -> + board = {'O'} + assert.are.equal 'O', Connect.winner board + + pending 'only edges does not make a winner', -> + board = { + 'O O O X', + ' X . . X', + ' X . . X', + ' X O O O', + } + assert.are.equal '', Connect.winner board + + pending 'illegal diagonal does not make a winner', -> + board = { + 'X O . .', + ' O X X X', + ' O X O .', + ' . O X .', + ' X X O O', + } + assert.are.equal '', Connect.winner board + + pending 'nobody wins crossing adjacent angles', -> + board = { + 'X . . .', + ' . X O .', + ' O . X O', + ' . O . X', + ' . . O .', + } + assert.are.equal '', Connect.winner board + + pending 'X wins crossing from left to right', -> + board = { + '. O . .', + ' O X X X', + ' O X O .', + ' X X O X', + ' . O X .', + } + assert.are.equal 'X', Connect.winner board + + pending 'X wins with left-hand dead end fork', -> + board = { + '. . X .', + ' X X . .', + ' . X X X', + ' O O O O', + } + assert.are.equal 'X', Connect.winner board + + pending 'X wins with right-hand dead end fork', -> + board = { + '. . X X', + ' X X . .', + ' . X X .', + ' O O O O', + } + assert.are.equal 'X', Connect.winner board + + pending 'O wins crossing from top to bottom', -> + board = { + '. O . .', + ' O X X X', + ' O O O .', + ' X X O X', + ' . O X .', + } + assert.are.equal 'O', Connect.winner board + + pending 'X wins using a convoluted path', -> + board = { + '. X X . .', + ' X . X . X', + ' . X . X .', + ' . X X . .', + ' O O O O O', + } + assert.are.equal 'X', Connect.winner board + + pending 'X wins using a spiral path', -> + board = { + 'O X X X X X X X X', + ' O X O O O O O O O', + ' O X O X X X X X O', + ' O X O X O O O X O', + ' O X O X X X O X O', + ' O X O O O X O X O', + ' O X X X X X O X O', + ' O O O O O O O X O', + ' X X X X X X X X O', + } + assert.are.equal 'X', Connect.winner board