Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
language: python
python:
- "3.4"
# install: "pip install -r Boggle_pt_2/LessonCode/requirements.txt"
install: "pip install -r 03-bonus-serving-with-flask/requirements.txt"
script:
- 01-getting-started/test_boggle.py
- 01.c1-testing-larger-grids/test_boggle.py
- 02-optimising/test_boggle.py
- 02-optimising/test_boggle.py
- 03-bonus-serving-with-flask/test_boggle.py
1 change: 1 addition & 0 deletions 03-bonus-serving-with-flask/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.pyc
132 changes: 132 additions & 0 deletions 03-bonus-serving-with-flask/boggle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env python
import os
from string import ascii_uppercase
from random import choice

SCRIPT_PATH = os.path.join(os.getcwd(), os.path.dirname(__file__))


def make_grid(width, height):
"""
Create a grid that will hold all of the tiles for a boggle game
"""
return {(row, col): choice(ascii_uppercase)
for row in range(height) for col in range(width)}


def neighbours_of_position(coords):
"""
Get neighbours of a given position
"""

row = coords[0]
col = coords[1]

# Assign each of the neighbours

# Top-left to top-right
top_left = (row - 1, col - 1)
top_center = (row - 1, col)
top_right = (row - 1, col + 1)

# Left to right
left = (row, col - 1)
# The `(row, col)` coordinates passed to this
# function are situated here
right = (row, col + 1)

# Bottom-left to bottom-right
bottom_left = (row + 1, col - 1)
bottom_center = (row + 1, col)
bottom_right = (row + 1, col + 1)

return [top_left, top_center, top_right,
left, right,
bottom_left, bottom_center, bottom_right]


def all_grid_neighbours(grid):
"""
Get all of the possible neighbours for each position in
the grid
"""
neighbours = {}
for position in grid:
position_neighbours = neighbours_of_position(position)
neighbours[position] = [p for p in position_neighbours if p in grid]
return neighbours


def path_to_word(grid, path):
"""
Add all of the letters on the path to a string
"""
return ''.join([grid[p] for p in path])


def search(grid, dictionary):
neighbours = all_grid_neighbours(grid)
paths = []
full_words, stems = dictionary

def do_search(path):
word = path_to_word(grid, path)
if word in full_words:
paths.append(path)
if word not in stems:
return
for next_pos in neighbours[path[-1]]:
if next_pos not in path:
do_search(path + [next_pos])

for position in grid:
do_search([position])

words = []
for path in paths:
words.append(path_to_word(grid, path))
return set(words)


def get_dictionary(dictionary_file):
"""
Load Dictionary file
"""
if not dictionary_file.startswith('/'):
# If not absolute, then make path relative to our location:
dictionary_file = os.path.join(SCRIPT_PATH, dictionary_file)

full_words, stems = set(), set()

with open(dictionary_file) as f:
for word in f:
word = word.strip().upper()
full_words.add(word)

for i in range(1, len(word)):
stems.add(word[:i])

return full_words, stems


def display_words(words):
for word in words:
print(word)
print("Found %s words" % len(words))


def main():
"""
This is the function that will run the whole project
"""
# Here you can change your grid from a 3x3 to a 2x2 to test run times
grid = make_grid(4, 4)

dictionary = get_dictionary("words.txt")
words = search(grid, dictionary)
display_words(words)


if __name__ == "__main__":
# Code in here will only execution when the file is run directly
main()
29 changes: 29 additions & 0 deletions 03-bonus-serving-with-flask/boggle_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python
import os

from flask import Flask
from flask import render_template

import boggle

app = Flask(__name__)


@app.route("/")
def boggle_page():
ROWS, COLS = 4, 4
grid = boggle.make_grid(ROWS, COLS)
dictionary = boggle.get_dictionary('words.txt')
words = boggle.search(grid, dictionary)

# to simplify the template, we'll convert the grid into a list
grid_list = []
for row in range(ROWS):
grid_list.append([grid[(row, col)] for col in range(COLS)])
return render_template("boggle.html", grid=grid_list, words=words)


if __name__ == "__main__":
app.run(host=os.getenv('IP', '0.0.0.0'),
port=int(os.getenv('PORT', '8080')),
debug=True)
1 change: 1 addition & 0 deletions 03-bonus-serving-with-flask/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Flask==0.12.2
41 changes: 41 additions & 0 deletions 03-bonus-serving-with-flask/templates/boggle.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Boggle Solver</title>
<style>
#grid table {
table-layout: fixed;
border-collapse: collapse;
}
#grid td {
border: 1px solid black;
text-align: center;
}
</style>
</head>
<body>
<h1>Our Boggle Solver</h1>

<section id="grid">
<h2>Letter Grid:</h2>
<table>
{% for row in grid %}
<tr>
{% for letter in row %}
<td>{{ letter }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</section>

<section id="words">
<h2>Words Found:</h2>
<ol>
{% for word in words %}
<li>{{ word }}</li>
{% endfor %}
</ol>
</section>
</body>
</html>
108 changes: 108 additions & 0 deletions 03-bonus-serving-with-flask/test_boggle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python
import unittest
import boggle
from string import ascii_uppercase


class TestBoggle(unittest.TestCase):
"""
Our test suite for boggle solver
"""

def test_can_create_an_empty_grid(self):
"""
Ensure that we can create an empty boggle grid
"""
grid = boggle.make_grid(0, 0)
self.assertEqual(len(grid), 0)

def test_grid_size_is_width_times_height(self):
"""
Ensure that the total size of the grid is equal to width * height
"""
grid = boggle.make_grid(2, 3)
self.assertEqual(len(grid), 6)

def test_grid_coordinates(self):
"""
Ensure that all of the coordinates inside of the grid can be accessed
"""
grid = boggle.make_grid(2, 2)
self.assertIn((0, 0), grid)
self.assertIn((0, 1), grid)
self.assertIn((1, 0), grid)
self.assertIn((1, 1), grid)
self.assertNotIn((2, 2), grid)

def test_grid_is_filled_with_letters(self):
"""
Ensure that each of the coordinates in the grid contains letters
"""
grid = boggle.make_grid(2, 3)
for letter in grid.values():
self.assertIn(letter, ascii_uppercase)

def test_neighbours_of_a_position(self):
"""
Ensure that a position has 8 neighbours
"""
coords = (1, 2)
neighbours = boggle.neighbours_of_position(coords)
self.assertIn((0, 1), neighbours)
self.assertIn((0, 2), neighbours)
self.assertIn((0, 3), neighbours)
self.assertIn((1, 1), neighbours)
self.assertIn((1, 3), neighbours)
self.assertIn((2, 1), neighbours)
self.assertIn((2, 2), neighbours)
self.assertIn((2, 3), neighbours)

def test_all_grid_neighbours(self):
"""
Ensure that all of the grid positions have neighbours
"""
grid = boggle.make_grid(2, 2)
neighbours = boggle.all_grid_neighbours(grid)
self.assertEqual(len(neighbours), len(grid))
for pos in grid:
others = list(grid) # create a list from the dictionary's keys
others.remove(pos)
self.assertListEqual(sorted(neighbours[pos]), sorted(others))

def test_converting_a_path_to_a_word(self):
"""
Ensure that paths can be converted to words
"""
grid = boggle.make_grid(2, 2)
oneLetterWord = boggle.path_to_word(grid, [(0, 0)])
twoLetterWord = boggle.path_to_word(grid, [(0, 0), (1, 1)])
self.assertEqual(oneLetterWord, grid[(0, 0)])
self.assertEqual(twoLetterWord, grid[(0, 0)] + grid[(1, 1)])

def test_search_grid_for_words(self):
grid = {(0, 0): 'A', (0, 1): 'B', (1, 0): 'C', (1, 1): 'D'}
twoLetterWord = 'AB'
threeLetterWord = 'ABC'
notThereWord = "EEE"

fullWords = [twoLetterWord, threeLetterWord, notThereWord]
stems = ['A', 'AB', 'E', 'EE']
dictionary = fullWords, stems

foundWords = boggle.search(grid, dictionary)

self.assertTrue(twoLetterWord in foundWords)
self.assertTrue(threeLetterWord in foundWords)
self.assertTrue(notThereWord not in foundWords)

def test_load_dictionary(self):
"""
Test that the `get_dictionary` function returns a dictionary
that has a length greater than 0
"""
dictionary = boggle.get_dictionary("words.txt")
self.assertGreater(len(dictionary), 0)


if __name__ == "__main__":
unittest.main()
Loading