Skip to content

Commit

Permalink
refactor exercise word-search
Browse files Browse the repository at this point in the history
* rewrite tests so that users have more freedom of implementation
* update example-implementation to fit test-interface
* reduce skeleton to minimal as discussed in exercism#272
  • Loading branch information
pheanex committed Mar 1, 2017
1 parent 02928df commit e66b447
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 143 deletions.
79 changes: 22 additions & 57 deletions exercises/word-search/example.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,22 @@
import copy


class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y

def __repr__(self):
return 'Point({}:{})'.format(self.x, self.y)

def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)

def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)

def __eq__(self, other):
return self.x == other.x and self.y == other.y

def __ne__(self, other):
return not(self == other)


DIRECTIONS = (Point(1, 0), Point(1, -1), Point(1, 1), Point(-1, -1),
Point(0, -1), Point(0, 1), Point(-1, 1), Point(-1, 0))


class WordSearch(object):
def __init__(self, puzzle):
self.rows = puzzle.split()
self.width = len(self.rows[0])
self.height = len(self.rows)

def find_char(self, coordinate):
if coordinate.x < 0 or coordinate.x >= self.width:
return
if coordinate.y < 0 or coordinate.y >= self.height:
return
return self.rows[coordinate.y][coordinate.x]

def find(self, word, position, direction):
current = copy.copy(position)
for letter in word:
if self.find_char(current) != letter:
return
current += direction
return position, current - direction

def search(self, word):
positions = (Point(x, y) for x in range(self.width) for y in range(self.height))
for pos in positions:
for d in DIRECTIONS:
result = self.find(word, pos, d)
if result:
return result
return None
def find_stop(row, column, word, puzzle):
directions = [(0, 1), (0, -1), (1, 0), (1, 1), (1, -1), (-1, 0), (-1, 1), (-1, -1)]
for d in directions:
row_nr, column_nr = row, column
for char_nr, char in enumerate(word):
try:
if puzzle[row_nr][column_nr] != char:
break
except IndexError:
break
if char_nr == len(word) - 1:
return row_nr, column_nr
row_nr += d[0]
column_nr += d[1]


def search(puzzle, word):
for row_nr, row in enumerate(puzzle):
for column_nr, char in enumerate(row):
stop = find_stop(row_nr, column_nr, word, puzzle)
if stop:
return (row_nr, column_nr), stop
29 changes: 2 additions & 27 deletions exercises/word-search/word_search.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,2 @@
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y

def __repr__(self):
return 'Point({}:{})'.format(self.x, self.y)

def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)

def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)

def __eq__(self, other):
return self.x == other.x and self.y == other.y

def __ne__(self, other):
return not(self == other)


class WordSearch(object):
def __init__(self, puzzle):
pass

def search(self, word):
return None
def search():
pass
87 changes: 28 additions & 59 deletions exercises/word-search/word_search_test.py
Original file line number Diff line number Diff line change
@@ -1,84 +1,53 @@
import unittest

from word_search import WordSearch, Point
from word_search import search


class WordSearchTests(unittest.TestCase):

@classmethod
def setUpClass(self):
puzzle = ('jefblpepre\n'
'camdcimgtc\n'
'oivokprjsm\n'
'pbwasqroua\n'
'rixilelhrs\n'
'wolcqlirpc\n'
'screeaumgr\n'
'alxhpburyi\n'
'jalaycalmp\n'
'clojurermt')
self.example = WordSearch(puzzle)
puzzle = ['jefblpepre',
'camdcimgtc',
'oivokprjsm',
'pbwasqroua',
'rixilelhrs',
'wolcqlirpc',
'screeaumgr',
'alxhpburyi',
'jalaycalmp',
'clojurermt']

def test_horizontal_words_left_to_right(self):
self.assertEqual(
self.example.search('clojure'),
(Point(0, 9), Point(6, 9))
)
self.assertEqual(search(WordSearchTests.puzzle, 'clojure'), ((9, 0), (9, 6)))

def test_horizontal_words_right_to_left(self):
self.assertEqual(
self.example.search('elixir'),
(Point(5, 4), Point(0, 4))
)
self.assertEqual(search(WordSearchTests.puzzle, 'elixir'), ((4, 5), (4, 0)))

def test_vertical_words_top_to_bottom(self):
self.assertEqual(
self.example.search('ecmascript'),
(Point(9, 0), Point(9, 9))
)
self.assertEqual(search(WordSearchTests.puzzle, 'ecmascript'), ((0, 9), (9, 9)))

def test_vertical_words_bottom_to_top(self):
self.assertEqual(
self.example.search('rust'),
(Point(8, 4), Point(8, 1))
)
self.assertEqual(search(WordSearchTests.puzzle, 'rust'), ((4, 8), (1, 8)))

def test_diagonal_words_top_left_to_bottom_right(self):
self.assertEqual(
self.example.search('java'),
(Point(0, 0), Point(3, 3))
)
def test_diagonal_top_left_to_bottom_right(self):
self.assertEqual(search(WordSearchTests.puzzle, 'java'), ((0, 0), (3, 3)))

def test_diagonal_upper_bottom_right_to_top_left(self):
self.assertEqual(
self.example.search('lua'),
(Point(7, 8), Point(5, 6))
)
def test_diagonal_bottom_right_to_top_left(self):
self.assertEqual(search(WordSearchTests.puzzle, 'lua'), ((8, 7), (6, 5)))

def test_diagonal_upper_bottom_left_to_top_right(self):
self.assertEqual(
self.example.search('lisp'),
(Point(2, 5), Point(5, 2))
)
def test_diagonal_bottom_left_to_top_right(self):
self.assertEqual(search(WordSearchTests.puzzle, 'lisp'), ((5, 2), (2, 5)))

def test_diagonal_upper_top_right_to_bottom_left(self):
self.assertEqual(
self.example.search('ruby'),
(Point(7, 5), Point(4, 8))
)
def test_diagonal_top_right_to_bottom_left(self):
self.assertEqual(search(WordSearchTests.puzzle, 'ruby'), ((5, 7), (8, 4)))

def test_words_that_are_not_in_the_puzzle(self):
self.assertIsNone(self.example.search('haskell'))
self.assertIsNone(search(WordSearchTests.puzzle, 'haskell'))

def test_search_differently_sized_puzzles(self):
puzzle = ('qwertyuiopz\n'
'luamsicrexe\n'
'abcdefghijk')
self.assertEqual(
WordSearch(puzzle).search('exercism'),
(Point(10, 1), Point(3, 1))
)

puzzle = ['qwertyuiopz',
'luamsicrexe',
'abcdefghijk']
self.assertEqual(search(puzzle, 'exercism'), ((1, 10), (1, 3)))

if __name__ == '__main__':
unittest.main()

0 comments on commit e66b447

Please sign in to comment.