From b2bc11ea383f4f393d62841949761d80d76873c1 Mon Sep 17 00:00:00 2001 From: Yunchih Chen Date: Sat, 7 Oct 2017 23:23:26 +0800 Subject: [PATCH] connect: Implement the exercise "connect" --- config.json | 11 +++ exercises/connect/README.md | 44 ++++++++++++ exercises/connect/connect.py | 4 ++ exercises/connect/connect_test.py | 110 ++++++++++++++++++++++++++++++ exercises/connect/example.py | 61 +++++++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 exercises/connect/README.md create mode 100644 exercises/connect/connect.py create mode 100644 exercises/connect/connect_test.py create mode 100644 exercises/connect/example.py diff --git a/config.json b/config.json index 1d22c21285..7caa4cb96f 100644 --- a/config.json +++ b/config.json @@ -813,6 +813,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..c74c1a3715 --- /dev/null +++ b/exercises/connect/connect.py @@ -0,0 +1,4 @@ + +class ConnectGame: + def __init__(self, board): + pass diff --git a/exercises/connect/connect_test.py b/exercises/connect/connect_test.py new file mode 100644 index 0000000000..a3df62311a --- /dev/null +++ b/exercises/connect/connect_test.py @@ -0,0 +1,110 @@ +import unittest +import connect + +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 SieveTest(unittest.TestCase): + def test_game(self): + for t in testcases: + winner = connect.play(t["board"]) + expected = t["winner"] if t["winner"] else "None" + got = winner if winner else "None" + self.assertEqual(winner, t["winner"], + "Test failed: %s, expected winner: %s, got: %s" % (t["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..94f34b09d0 --- /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: + return any([self.walk_board(player, 0, y) for y in range(self.height)]) + if player == self.white: + return any([self.walk_board(player, x, 0) for x in range(self.width)]) + + 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 + +def play(board): + game = ConnectGame(board) + return game.get_winner()