mirror of
https://github.com/Smaug123/hanabi-server
synced 2025-10-11 03:28:40 +00:00
Initial commit
This commit is contained in:
0
HanabiWeb/__init__.py
Normal file
0
HanabiWeb/__init__.py
Normal file
58
HanabiWeb/cache.py
Normal file
58
HanabiWeb/cache.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
"""
|
||||||
|
Utility functions for storing and retrieving Hanabi server data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
# Field names for each field
|
||||||
|
_players = "players"
|
||||||
|
_hands = "hands"
|
||||||
|
_discards = "discards"
|
||||||
|
_knowledge = "knowledge"
|
||||||
|
_lives = "lives"
|
||||||
|
|
||||||
|
_fieldnames = [_players, _hands, _discards, _knowledge, _lives]
|
||||||
|
|
||||||
|
|
||||||
|
class GameDataStore:
|
||||||
|
"""
|
||||||
|
Store complete information about a Hanabi game in a file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
self.filepath = path
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
with open(self.filepath) as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def replace(self, data):
|
||||||
|
with open(self.filepath) as f:
|
||||||
|
yaml.dump(data, f)
|
||||||
|
|
||||||
|
def replace_field(self, field, data):
|
||||||
|
existing = self.get()
|
||||||
|
existing[field] = data
|
||||||
|
self.replace(existing)
|
||||||
|
|
||||||
|
def append_to_field(self, field, data):
|
||||||
|
existing = self.get()
|
||||||
|
existing[field].append(data)
|
||||||
|
self.replace(existing)
|
||||||
|
|
||||||
|
def add_player(self, player_name):
|
||||||
|
self.append_to_field(_players, player_name)
|
||||||
|
|
||||||
|
def create(self, players):
|
||||||
|
"""
|
||||||
|
Create a new Hanabi game, storing the data in the given file.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {_players: players,
|
||||||
|
_hands: [],
|
||||||
|
_discards: [],
|
||||||
|
_knowledge: {"used": 0, "available": 8},
|
||||||
|
_lives: {"used": 0, "available": 3}}
|
||||||
|
self.replace(data)
|
20
HanabiWeb/card.py
Normal file
20
HanabiWeb/card.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
@enum.unique()
|
||||||
|
class HanabiColour(enum.Enum):
|
||||||
|
Red = enum.auto()
|
||||||
|
Green = enum.auto()
|
||||||
|
White = enum.auto()
|
||||||
|
Yellow = enum.auto()
|
||||||
|
Blue = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
|
class HanabiCard:
|
||||||
|
def __str__(self):
|
||||||
|
return "{} {}".format(self.rank, self.colour)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.colour = None
|
||||||
|
self.rank = None
|
||||||
|
|
106
HanabiWeb/hanabi.py
Normal file
106
HanabiWeb/hanabi.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask_restful import Resource, Api, abort
|
||||||
|
|
||||||
|
from . import hanabi_cache
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
api = Api(app)
|
||||||
|
|
||||||
|
|
||||||
|
_DATA_STORES = os.path.join(os.path.expanduser('~'), '.hanabigames')
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_game_id(game_id):
|
||||||
|
"""
|
||||||
|
Test whether a game ID is valid. If it is not, raise a 403 Forbidden.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
int(str(game_id))
|
||||||
|
except ValueError:
|
||||||
|
abort(403, message="Malformed game ID {}".format(game_id))
|
||||||
|
|
||||||
|
|
||||||
|
def _game_data_path(game_id):
|
||||||
|
"""
|
||||||
|
Find the path to the data file for a given game.
|
||||||
|
|
||||||
|
This fully trusts game_id, and is not safe on unsanitised input.
|
||||||
|
"""
|
||||||
|
return os.path.join(_DATA_STORES, "{}.dat".format(game_id))
|
||||||
|
|
||||||
|
|
||||||
|
def ls(directory):
|
||||||
|
onlyfiles = [f
|
||||||
|
for f in os.listdir(directory)
|
||||||
|
if os.path.isfile(os.path.join(directory, f))]
|
||||||
|
return onlyfiles
|
||||||
|
|
||||||
|
|
||||||
|
def _get_new_game_index():
|
||||||
|
files = ls(_DATA_STORES)
|
||||||
|
indices = [int(name.rstrip('.dat'))
|
||||||
|
for name in files
|
||||||
|
if re.match(r"[0-9]+\.dat$", name)]
|
||||||
|
if not indices:
|
||||||
|
return 0
|
||||||
|
indices.sort()
|
||||||
|
return indices[-1] + 1
|
||||||
|
|
||||||
|
|
||||||
|
class Hand(Resource):
|
||||||
|
def get(self, game_id, player_id):
|
||||||
|
return {'hello': 'world'}
|
||||||
|
|
||||||
|
|
||||||
|
class Play(Resource):
|
||||||
|
def post(self, game_id, player_id, card):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Game(Resource):
|
||||||
|
def get(self, game_id, player_id=None):
|
||||||
|
"""
|
||||||
|
Return the state of the game as viewed by the given player.
|
||||||
|
|
||||||
|
If no player is specified, return the state of the game as viewed by a
|
||||||
|
spectator.
|
||||||
|
|
||||||
|
:param game_id: Lookup ID for the given game.
|
||||||
|
:param player_id: Lookup ID for a certain player in this game.
|
||||||
|
:return: Dictionary of game state.
|
||||||
|
{players: [players],
|
||||||
|
hands: {player1: [cards], player2: [cards]},
|
||||||
|
discards: [cards],
|
||||||
|
knowledge: {used: 5, available: 3},
|
||||||
|
lives: {used: 0, available: 3}}
|
||||||
|
"""
|
||||||
|
_validate_game_id(game_id)
|
||||||
|
data_path = _game_data_path(game_id)
|
||||||
|
|
||||||
|
data = hanabi_cache.GameDataStore(data_path)
|
||||||
|
|
||||||
|
if player_id is None:
|
||||||
|
return data.get()
|
||||||
|
|
||||||
|
def put(self, players):
|
||||||
|
"""
|
||||||
|
Create a new game, returning the game ID.
|
||||||
|
"""
|
||||||
|
new_id = _get_new_game_index()
|
||||||
|
data_path = _game_data_path(new_id)
|
||||||
|
data = hanabi_cache.GameDataStore(data_path)
|
||||||
|
data.create(players)
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(Hand, '/hanabi')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/hanabi")
|
||||||
|
def hello():
|
||||||
|
return "Hello, World!"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True)
|
0
HanabiWeb/requirements.txt
Normal file
0
HanabiWeb/requirements.txt
Normal file
Reference in New Issue
Block a user