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