initial code commit
This commit is contained in:
parent
52c8fa4bc8
commit
00e52ad35f
9 changed files with 348 additions and 0 deletions
47
bot.py
Normal file
47
bot.py
Normal 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
68
card.py
Normal 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
1
credentials.py
Normal file
|
@ -0,0 +1 @@
|
|||
TOKEN = 'TOKEN'
|
39
deck.py
Normal file
39
deck.py
Normal 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
53
game.py
Normal 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
29
game_manager.py
Normal 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
70
player.py
Normal 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
0
test/__init__.py
Normal file
41
test/test.py
Normal file
41
test/test.py
Normal 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)
|
Loading…
Reference in a new issue