diff --git a/config.json b/config.json index 61178a3b29..db6c3faab2 100644 --- a/config.json +++ b/config.json @@ -1021,6 +1021,17 @@ "text_formatting" ] }, + { + "uuid": "f5503274-ac23-11e7-abc4-cec278b6b50a", + "slug": "connect", + "core": false, + "unlocked_by": null, + "difficulty": 1, + "topics": [ + "parsing", + "transforming" + ] + }, { "uuid": "33f689ee-1d9c-4908-a71c-f84bff3510df", "slug": "collatz-conjecture", diff --git a/exercises/connect/README.md b/exercises/connect/README.md new file mode 100644 index 0000000000..4eb6d1a9d5 --- /dev/null +++ b/exercises/connect/README.md @@ -0,0 +1,44 @@ +# Connect + +Compute the result for a game of Hex / Polygon. + +The abstract boardgame known as +[Hex](https://en.wikipedia.org/wiki/Hex_%28board_game%29) / Polygon / +CON-TAC-TIX is quite simple in rules, though complex in practice. Two players +place stones on a rhombus with hexagonal fields. The player to connect his/her +stones to the opposite side first wins. The four sides of the rhombus 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.) + +The boards look like this (with spaces added for readability, which won't be in +the representation passed to your code): + +``` +. 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. + +### Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. + +For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /python/bob/bob.py`. + + +For more detailed information about running tests, code style and linting, +please see the [help page](http://exercism.io/languages/python). + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/connect/connect.py b/exercises/connect/connect.py new file mode 100644 index 0000000000..688dd688ae --- /dev/null +++ b/exercises/connect/connect.py @@ -0,0 +1,7 @@ + +class ConnectGame: + def __init__(self, board): + pass + + def get_winner(self): + pass diff --git a/exercises/connect/connect_test.py b/exercises/connect/connect_test.py new file mode 100644 index 0000000000..f199f88b86 --- /dev/null +++ b/exercises/connect/connect_test.py @@ -0,0 +1,118 @@ +import unittest + +import connect + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + +testcases = [ + { + "description": "an empty board has no winner", + "board": + """ . . . . . + . . . . . + . . . . . + . . . . . + . . . . .""", + "winner": "" + }, + { + "description": "O can win on a 1x1 board", + "board": "O", + "winner": "O" + }, + { + "description": "X can win on a 1x1 board", + "board": "X", + "winner": "X" + }, + { + "description": "only edges does not make a winner", + "board": + """ O O O X + X . . X + X . . X + X O O O""", + "winner": "" + }, + { + "description": "illegal diagonal does not make a winner", + "board": + """ X O . . + O X X X + O X O . + . O X . + X X O O""", + "winner": "" + }, + { + "description": "nobody wins crossing adjacent angles", + "board": + """ X . . . + . X O . + O . X O + . O . X + . . O .""", + "winner": "" + }, + { + "description": "X wins crossing from left to right", + "board": + """ . O . . + O X X X + O X O . + X X O X + . O X .""", + "winner": "X" + }, + { + "description": "X wins using a convoluted path", + "board": + """ . X X . . + X . X . X + . X . X . + . X X . . + O O O O O""", + "winner": "X" + }, + { + "description": "O wins crossing from top to bottom", + "board": + """ . O . . + O X X X + O O O . + X X O X + . O X .""", + "winner": "O" + }, + { + "description": "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 """, + "winner": "X" + }, +] + + +class ConnectTest(unittest.TestCase): + def test_game(self): + for testcase in testcases: + game = connect.ConnectGame(testcase["board"]) + winner = game.get_winner() + expected = testcase["winner"] if testcase["winner"] else "None" + got = winner if winner else "None" + self.assertEqual(winner, testcase["winner"], + "Test failed: %s, expected winner: %s, got: %s" + % (testcase["description"], expected, got)) + + +if __name__ == '__main__': + unittest.main() diff --git a/exercises/connect/example.py b/exercises/connect/example.py new file mode 100644 index 0000000000..be8d98e3f0 --- /dev/null +++ b/exercises/connect/example.py @@ -0,0 +1,61 @@ + +class ConnectGame: + + directions = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, -1), (-1, 1)] + white = "O" + black = "X" + none = "" + + def __init__(self, lines): + self.board = self.make_board(lines) + assert len(self.board) > 0 + + self.width = len(self.board[0]) + self.height = len(self.board) + assert self.width > 0 and self.height > 0 + + for l in self.board: + assert len(l) == self.width + + def valid(self, x, y): + return x >= 0 and x < self.width and y >= 0 and y < self.height + + def make_board(self, lines): + return ["".join(l.split()) for l in lines.splitlines()] + + def player_reach_dest(self, player, x, y): + if player == self.black: + return x == self.width - 1 + if player == self.white: + return y == self.height - 1 + + def walk_board(self, player, x, y, visited=[]): + if (x, y) in visited: + return False + + if (not self.valid(x, y)) or self.board[y][x] != player: + return False + + if self.player_reach_dest(player, x, y): + return True + + for d in self.directions: + if self.walk_board(player, x + d[0], y + d[1], visited + [(x, y)]): + return True + + def check_player_is_winner(self, player): + if player == self.black: + for y in range(self.height): + if self.walk_board(player, 0, y): + return True + if player == self.white: + for x in range(self.width): + if self.walk_board(player, x, 0): + return True + + def get_winner(self): + if self.check_player_is_winner(self.black): + return self.black + if self.check_player_is_winner(self.white): + return self.white + return self.none