Skip to content
Merged
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
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ ALL_FLAG := $(ENV)/.all

# Main Targets #################################################################

IP = $(shell ipconfig getifaddr en0)

.PHONY: all
all: depends doc $(ALL_FLAG)
$(ALL_FLAG): $(SOURCES)
Expand Down Expand Up @@ -103,7 +105,7 @@ run-public: env

.PHONY: launch-public
launch-public: env
eval "sleep 1; $(OPEN) http://localhost:8000" &
eval "sleep 1; $(OPEN) http://$(IP):5000" &
$(MAKE) run-public

.PHONY: watch
Expand Down Expand Up @@ -217,7 +219,7 @@ fix: depends-dev

RANDOM_SEED ?= $(shell date +%s)

PYTEST_CORE_OPTS := --doctest-modules --verbose -r X --maxfail=3
PYTEST_CORE_OPTS := --doctest-modules --verbose -r X -vv
PYTEST_COV_OPTS := --cov=$(PACKAGE) --cov-report=term-missing --no-cov-on-fail
PYTEST_RANDOM_OPTS := --random --random-seed=$(RANDOM_SEED)

Expand Down
File renamed without changes.
19 changes: 19 additions & 0 deletions data/games/02_added.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
board: {}
players:
- code: '1111'
color: red
turn:
done: false
moves: []
- code: '2222'
color: blue
turn:
done: false
moves: []
- code: '3333'
color: teal
turn:
done: false
moves: []
timestamp: 0
turn: 0
9 changes: 5 additions & 4 deletions data/games/_started_.yml → data/games/03_started.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
players:
- code: '1234'
color: red
turns:
- done: false
turn:
done: false
moves: []
- code: '5678'
color: blue
turns:
- done: false
turn:
done: false
moves: []
timestamp: 42
turn: 1
14 changes: 14 additions & 0 deletions data/games/04_playing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
board: {}
players:
- code: '1234'
color: red
turn:
done: true
moves: []
- code: '5678'
color: blue
turn:
done: false
moves: []
timestamp: 42
turn: 3
3 changes: 2 additions & 1 deletion gridcommand/domain/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Domain models for the application."""

from .move import Move, Moves
from .turn import Turn, Turns
from .turn import Turn
from .player import Player, Players
from .board import Board
from .game import Game
26 changes: 26 additions & 0 deletions gridcommand/domain/board.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Classes representing boards."""

import copy

from .. import common


log = common.logger(__name__)


class Board:

"""The board for a game."""

@classmethod
def randomize(cls, players):
"""Create a new random board for the players."""
# TODO: implement randomize
print(players)
return cls()

def update(self, players):
"""Update the board state from the player's moves."""
# TODO: implement update
print(players)
return copy.copy(self)
39 changes: 30 additions & 9 deletions gridcommand/domain/game.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Classes representing games."""

import time
import string
import random

from .. import common
from .player import Players
from .turn import Turn
from .board import Board

log = common.logger(__name__)

Expand All @@ -17,10 +18,12 @@ class Game:
KEY_CHARS = string.ascii_lowercase + string.digits
KEY_LENGTH = 8

def __init__(self, key=None):
def __init__(self, key=None, timestamp=None):
self.key = key or self._generate_key()
self.timestamp = timestamp or self._get_timestamp()
self.players = Players()
self.turn = 0
self.board = None

def __repr__(self):
return "<game: {}>".format(self.key)
Expand All @@ -36,6 +39,10 @@ def _generate_key():
return ''.join(random.choice(Game.KEY_CHARS)
for _ in range(Game.KEY_LENGTH))

@staticmethod
def _get_timestamp():
return int(time.time())

def create_player(self, code, exc=ValueError):
if self.started:
raise exc("Game has already started.")
Expand All @@ -51,15 +58,29 @@ def started(self):
return self.turn > 0

def start(self, exc=ValueError):
if len(self.players) < 2:
"""Populate the game board."""
log.info("starting the game...")
if self.started:
raise exc("The game has already started.")
elif len(self.players) < 2:
raise exc("At least 2 players are required.")
if self.turn == 0:
self.advance()
else:
self.board = Board.randomize(self.players)
self.turn = 1

def advance(self):
def advance(self, exc=ValueError):
"""Start the next turn."""
log.info("starting the next turn...")
if not self.started:
raise exc("The game has not started.")

# End every players turn
for player in self.players:
player.turn.done = True

# TODO: update the board

# Start the next turn
self.turn += 1
for player in self.players:
if player.turns.current:
player.turns.current.done = True
player.turns.append(Turn())
player.turn.done = False
4 changes: 2 additions & 2 deletions gridcommand/domain/player.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Classes representing players in a game."""

from ..common import logger
from .turn import Turns
from .turn import Turn


log = logger(__name__)
Expand All @@ -15,7 +15,7 @@ def __init__(self, color, code=''):
super().__init__()
self.color = color
self.code = code
self.turns = Turns()
self.turn = Turn()

def __repr__(self):
return "<player: {}>".format(self.color)
Expand Down
33 changes: 8 additions & 25 deletions gridcommand/domain/turn.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,15 @@ class Turn:

"""An individual turn for a player."""

def __init__(self):
super().__init__()
def __init__(self, done=False):
self.moves = Moves()
self.done = False
self.done = done

def __repr__(self):
return "<turn>"
return "<turn: {}>".format("finished" if self.done else "started")


class Turns(list):

"""A list of turns in a game for each player."""

def __repr__(self):
return "<{} turn{}>".format(len(self), "" if len(self) == 1 else "s")

@property
def current(self):
"""Get the most recent turn."""
try:
return self[-1]
except IndexError:
return None

def find(self, number, exc=ValueError):
try:
return self[number - 1]
except IndexError:
raise exc("The turn '{}' does not exist.".format(number))
def finish(self, exc=ValueError):
"""Mark the current turn as complete."""
if self.done:
raise exc("The turn has already finished.")
self.done = True
1 change: 1 addition & 0 deletions gridcommand/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
app.service.exceptions.not_found = exceptions.NotFound
app.service.exceptions.permission_denied = exceptions.PermissionDenied
app.service.exceptions.missing_input = exceptions.ParseError
app.service.exceptions.authentication_failed = exceptions.AuthenticationFailed

# TODO: replace imports with blueprints
from . import root, game, player, turn, move # noqa, loads routes
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Formats domain objects for route responses."""

from collections import OrderedDict

from flask import url_for

from .base import Formatter
from ._bases import Formatter


# TODO: figure out a better way to serialize objects without parent objects
Expand All @@ -14,14 +16,16 @@ class GameFormatter(Formatter):
"""Serializes games into dictionaries."""

def format_single(self, game):
data = OrderedDict()

kwargs = dict(_external=True, key=game.key)
game_url = url_for('.games_detail', **kwargs)
players_url = url_for('.players_list', **kwargs)
start_url = url_for('.games_start', **kwargs)
return {'uri': game_url,
'players': players_url,
'start': start_url,
'turn': game.turn}
data['uri'] = url_for('.games_detail', **kwargs)
data['timestamp'] = game.timestamp
data['players'] = url_for('.players_list', **kwargs)
data['turn'] = game.turn
data['start'] = url_for('.games_start', **kwargs)

return data

def format_multiple(self, games):
return [url_for('.games_detail',
Expand All @@ -33,38 +37,51 @@ class PlayerFormatter(Formatter):
"""Serializes players into dictionaries."""

def format_single(self, player, game, auth):
data = {'turn': len(player.turns)}
data = OrderedDict()

kwargs = dict(_external=True, key=game.key, color=player.color)
if auth:
kwargs.update(code=player.code)
player_url = url_for('.players_detail', **kwargs)
turns_url = url_for('.turns_list', **kwargs)
data['turns'] = turns_url
else:
player_url = url_for('.players_detail', **kwargs)
data['uri'] = player_url
data['uri'] = url_for('.players_detail', **kwargs)
data['done'] = player.turn.done
if auth:
data['turns'] = url_for('.turns_list', **kwargs)

return data

def format_multiple(self, players, game):
return [url_for('.players_detail', _external=True,
key=game.key, color=player.color) for player in players]


class BoardFormatter(Formatter):

def format_single(self, board):
data = OrderedDict()

# TODO: format board
print(board)

return data


class TurnFormatter(Formatter):

"""Serializes turns into dictionaries."""

def format_single(self, turn, game, player, number):
def format_single(self, game, player, number):
data = OrderedDict()

kwargs = dict(_external=True,
key=game.key,
color=player.color,
code=player.code,
number=number)
turn_url = url_for('.turns_detail', **kwargs)
moves_url = url_for('.moves_list', **kwargs)
return {'uri': turn_url,
'moves': moves_url,
'done': turn.done}
data['uri'] = url_for('.turns_detail', **kwargs)
data['moves'] = url_for('.moves_list', **kwargs)
data['finish'] = url_for('.turns_finish', **kwargs)

return data

def format_multiple(self, turns, game, player):
return [url_for('.turns_detail', _external=True,
Expand All @@ -77,7 +94,11 @@ class MoveFormatter(Formatter):
"""Serializes moves into dictionaries."""

def format_single(self, move):
return {'count': move.count}
data = OrderedDict()

data['count'] = move.count

return data

def format_multiple(self, moves, game, player):
return [url_for('.moves_detail', _external=True,
Expand All @@ -87,5 +108,6 @@ def format_multiple(self, moves, game, player):

game_formatter = GameFormatter()
player_formatter = PlayerFormatter()
board_formatter = BoardFormatter()
turn_formatter = TurnFormatter()
move_formatter = MoveFormatter()
Loading