initial code commit

This commit is contained in:
Jannes Höke 2016-02-28 23:57:24 +01:00
parent 52c8fa4bc8
commit 00e52ad35f
9 changed files with 348 additions and 0 deletions

47
bot.py Normal file
View file

@ -0,0 +1,47 @@
from telegram import Updater, InlineQueryResultPhoto
from game_manager import GameManager
import card as c
from credentials import TOKEN
gm = GameManager()
u = Updater(TOKEN)
dp = u.dispatcher
def new_game(bot, update):
chat_id = update.message.chat_id
link = gm.generate_invite_link(u.bot.getMe().username, chat_id)
bot.sendMessage(chat_id,
text="Click this link to join the game: %s" % link)
def start(bot, update, args):
if args:
gm.join_game(args[0], update.message.from_user)
else:
bot.sendMessage(update.message.chat_id,
text="Please invite me to a group and "
"issue the /start command there.")
def inline(bot, update):
if update.inline_query:
user_id = update.inline_query.from_user.id
player = gm.userid_player[user_id]
playable = list()
for card in player.playable_cards():
playable.append(
InlineQueryResultPhoto(str(card),
card.get_image_link(),
card.get_thumb_link())
)
bot.answerInlineQuery(update.inline_query.id, playable)
else:
user_id = update.chosen_inline_result.from_user.id
game = gm.userid_game[user_id]
game.play_card(c.from_str(update.chosen_inline_result.id))

68
card.py Normal file
View file

@ -0,0 +1,68 @@
# Colors
RED = 'r'
BLUE = 'b'
GREEN = 'g'
YELLOW = 'y'
COLORS = (RED, BLUE, GREEN, YELLOW)
# Values
ZERO = '0'
ONE = '1'
TWO = '2'
THREE = '3'
FOUR = '4'
FIVE = '5'
SIX = '6'
SEVEN = '7'
EIGHT = '8'
NINE = '9'
DRAW_TWO = 'draw'
REVERSE = 'reverse'
SKIP = 'skip'
VALUES = (ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, DRAW_TWO,
REVERSE, SKIP)
# Special cards
CHOOSE = 'colorchooser'
DRAW_FOUR = 'draw_four'
SPECIALS = (CHOOSE, DRAW_FOUR)
IMAGE_PATTERN = 'https://raw.githubusercontent.com/jh0ker/mau_mau_bot/' \
'master/images/jpg/%s.jpg'
THUMB_PATTERN = 'https://raw.githubusercontent.com/jh0ker/mau_mau_bot/' \
'master/images/thumb/%s.jpg'
class Card(object):
def __init__(self, color, value, special=None):
self.color = color
self.value = value
self.special = special
def __str__(self):
if self.special:
return self.special
else:
return '%s_%s' % (self.color, self.value)
def __repr__(self):
return str(self)
def get_image_link(self):
return IMAGE_PATTERN % str(self)
def get_thumb_link(self):
return THUMB_PATTERN % str(self)
def from_str(string):
if '_' in string:
color, value = string.split('_')
return Card(color, value)
else:
return Card(None, None, string)

1
credentials.py Normal file
View file

@ -0,0 +1 @@
TOKEN = 'TOKEN'

39
deck.py Normal file
View file

@ -0,0 +1,39 @@
from random import shuffle
import card
from card import Card
import logging
class Deck(object):
def __init__(self):
self.cards = list()
self.graveyard = list()
self.logger = logging.getLogger(__name__)
for color in card.COLORS:
for value in card.VALUES:
self.cards.append(Card(color, value))
if not value == card.ZERO:
self.cards.append(Card(color, value))
for special in card.SPECIALS * 4:
self.cards.append(Card(None, None, special=special))
self.logger.debug(self.cards)
self.shuffle()
def shuffle(self):
self.cards = shuffle(self.cards)
def draw(self):
try:
return self.cards.pop()
except IndexError:
while len(self.graveyard):
self.cards.append(self.graveyard.pop())
self.shuffle()
return self.draw()
def dismiss(self, card):
self.graveyard.append(card)

53
game.py Normal file
View file

@ -0,0 +1,53 @@
from deck import Deck
from card import Card
import card as c
from player import Player
class Game(object):
""" This class represents a game of mau mau
:type current_player: Player
"""
current_player = None
reversed = False
draw_counter = 0
choosing_color = False
def __init__(self):
self.deck = Deck()
self.last_card = self.deck.draw()
def reverse(self):
self.reversed = not self.reversed
def turn(self):
self.current_player = self.current_player.next
def play_card(self, card):
"""
:param card:
:type card: Card
:return:
"""
self.deck.dismiss(self.last_card)
self.last_card = card
if card.value is c.SKIP:
self.current_player = self.current_player.next.next
elif card.special is c.DRAW_FOUR:
self.draw_counter += 4
elif card.value is c.DRAW_TWO:
self.draw_counter += 2
elif card.value is c.REVERSE:
self.reverse()
if card.special not in (c.CHOOSE, c.DRAW_FOUR):
self.current_player = self.current_player.next
else:
self.choosing_color = True
def choose_color(self, color):
self.last_card.color = color
self.current_player = self.current_player.next
self.choosing_color = False

29
game_manager.py Normal file
View file

@ -0,0 +1,29 @@
from uuid import uuid4
from game import Game
from player import Player
LINK_PATTERN = 'https://telegram.me/%s?start=%s'
class GameManager(object):
def __init__(self):
self.gameid_game = dict()
self.userid_game = dict()
self.chatid_gameid = dict()
self.userid_user = dict()
self.userid_player = dict()
def generate_invite_link(self, bot_name, chat_id):
game_id = uuid4()
game = Game()
self.gameid_game[game_id] = game
self.chatid_gameid[chat_id] = game_id
return LINK_PATTERN % (bot_name, game_id)
def join_game(self, game_id, user):
game = self.gameid_game[game_id]
player = Player(game, user)
self.userid_player[user.id] = player
self.userid_game[user.id] = game

70
player.py Normal file
View file

@ -0,0 +1,70 @@
import card as c
class Player(object):
def __init__(self, game, user):
"""
:param game:
:type game Game
:return:
"""
self.cards = list()
self.game = game
self.user = user
if game.current_player:
self.next = game.current_player
self.prev = game.current_player.prev
game.current_player.prev.next = self
game.current_player.prev = self
else:
self._next = self
self._prev = self
game.current_player = self
for i in range(6):
self.cards.append(self.game.deck.draw())
def __repr__(self):
return repr(self.user)
def __str__(self):
return str(self.user)
@property
def next(self):
return self._next if not self.game.reversed else self._prev
@next.setter
def next(self, player):
if not self.game.reversed:
self._next = player
else:
self._prev = player
@property
def prev(self):
return self._prev if not self.game.reversed else self._next
@prev.setter
def prev(self, player):
if not self.game.reversed:
self._prev = player
else:
self._next = player
def playable_cards(self):
if self.game.current_player is not self:
return False
playable = list()
last = self.game.last_card
for card in self.cards:
if (card.color is last.color or card.value is last.value) and \
not last.special:
playable.append(card)
return playable

0
test/__init__.py Normal file
View file

41
test/test.py Normal file
View file

@ -0,0 +1,41 @@
import unittest
from game import Game
from player import Player
class Test(unittest.TestCase):
game = None
def setUp(self):
self.game = Game(0)
def test_insert(self):
p0 = Player(self.game, "Player 0")
p1 = Player(self.game, "Player 1")
p2 = Player(self.game, "Player 2")
self.assertEqual(p0, p2.next)
self.assertEqual(p1, p0.next)
self.assertEqual(p2, p1.next)
self.assertEqual(p0.prev, p2)
self.assertEqual(p1.prev, p0)
self.assertEqual(p2.prev, p1)
def test_reverse(self):
p0 = Player(self.game, "Player 0")
p1 = Player(self.game, "Player 1")
p2 = Player(self.game, "Player 2")
self.game.reverse()
p3 = Player(self.game, "Player 3")
self.assertEqual(p0, p3.next)
self.assertEqual(p1, p2.next)
self.assertEqual(p2, p0.next)
self.assertEqual(p3, p1.next)
self.assertEqual(p0, p2.prev)
self.assertEqual(p1, p3.prev)
self.assertEqual(p2, p1.prev)
self.assertEqual(p3, p0.prev)