add more fun
This commit is contained in:
parent
a684ed6505
commit
2798a3dc22
4 changed files with 188 additions and 14 deletions
37
data.py
Normal file
37
data.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from peewee import *
|
||||||
|
|
||||||
|
db = SqliteDatabase('tgmsbot.db', pragmas={
|
||||||
|
'journal_mode': 'wal',
|
||||||
|
'cache_size': -32 * 1000})
|
||||||
|
|
||||||
|
class Player(Model):
|
||||||
|
user_id = IntegerField(unique=True, primary_key=True)
|
||||||
|
mines = IntegerField()
|
||||||
|
death = IntegerField()
|
||||||
|
wins = IntegerField()
|
||||||
|
restricted_until = IntegerField()
|
||||||
|
immunity_cards = IntegerField()
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
db.close()
|
||||||
|
@staticmethod
|
||||||
|
def db_close():
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
db.connect()
|
||||||
|
db.create_tables([Player])
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
def get_player(user_id):
|
||||||
|
db.connect()
|
||||||
|
player = Player.get_or_none(Player.user_id == user_id)
|
||||||
|
if player is None:
|
||||||
|
player = Player.create(user_id=user_id, mines=0, death=0, wins=0,
|
||||||
|
restricted_until=0, immunity_cards=0)
|
||||||
|
return player
|
||||||
|
else:
|
||||||
|
return player
|
29
data_ram.py
Normal file
29
data_ram.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
pool = dict()
|
||||||
|
|
||||||
|
class Player():
|
||||||
|
def __init__(self, user_id, mines, death, wins, restricted_until, immunity_cards):
|
||||||
|
self.user_id = user_id
|
||||||
|
self.mines = mines
|
||||||
|
self.death = death
|
||||||
|
self.wins = wins
|
||||||
|
self.restricted_until = restricted_until
|
||||||
|
self.immunity_cards = immunity_cards
|
||||||
|
@staticmethod
|
||||||
|
def save():
|
||||||
|
pass
|
||||||
|
@staticmethod
|
||||||
|
def db_close():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_player(user_id):
|
||||||
|
player = pool.get(user_id, None)
|
||||||
|
if player is None:
|
||||||
|
player = Player(user_id=user_id, mines=0, death=0, wins=0,
|
||||||
|
restricted_until=0, immunity_cards=0)
|
||||||
|
pool[user_id] = player
|
||||||
|
return player
|
||||||
|
else:
|
||||||
|
return player
|
|
@ -46,6 +46,10 @@ class Board():
|
||||||
self.mmap = None
|
self.mmap = None
|
||||||
self.moves = list()
|
self.moves = list()
|
||||||
self.state = 0 # 0:not playing, 1:playing, 2:win, 3:dead
|
self.state = 0 # 0:not playing, 1:playing, 2:win, 3:dead
|
||||||
|
# statistics
|
||||||
|
self.__op = 0
|
||||||
|
self.__is = 0
|
||||||
|
self.__3bv = 0
|
||||||
def __gen_map(self, first_move):
|
def __gen_map(self, first_move):
|
||||||
height = self.height
|
height = self.height
|
||||||
width = self.width
|
width = self.width
|
||||||
|
@ -139,10 +143,9 @@ class Board():
|
||||||
self.__open(row, col)
|
self.__open(row, col)
|
||||||
|
|
||||||
def gen_statistics(self):
|
def gen_statistics(self):
|
||||||
|
if self.__op != 0:
|
||||||
|
return (self.__op, self.__is, self.__3bv)
|
||||||
self.__visited = np.zeros((self.height, self.width), dtype=np.int8)
|
self.__visited = np.zeros((self.height, self.width), dtype=np.int8)
|
||||||
self.__op = 0
|
|
||||||
self.__is = 0
|
|
||||||
self.__3bv = 0
|
|
||||||
def scan_open(row, col):
|
def scan_open(row, col):
|
||||||
self.__visited[row][col] = 1
|
self.__visited[row][col] = 1
|
||||||
for nbr_rc in self.__iter_neighbour(row, col):
|
for nbr_rc in self.__iter_neighbour(row, col):
|
||||||
|
|
127
tgmsbot.py
127
tgmsbot.py
|
@ -3,9 +3,12 @@
|
||||||
from mscore import Board, check_params
|
from mscore import Board, check_params
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
|
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
|
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, run_async
|
||||||
from telegram.error import TimedOut as TimedOutError
|
from telegram.error import TimedOut as TimedOutError
|
||||||
from numpy import array_equal
|
from numpy import array_equal
|
||||||
|
# If no peewee orm is installed, try `from data_ram import get_player`
|
||||||
|
from data import get_player
|
||||||
|
from random import randint, choice
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -32,11 +35,17 @@ WIN_TEXT_TEMPLATE = "哇所有奇怪的地方都被你打开啦…好羞羞\n" \
|
||||||
"地图:Op {s_op} / Is {s_is} / 3bv {s_3bv}\n操作总数 {ops_count}\n" \
|
"地图:Op {s_op} / Is {s_is} / 3bv {s_3bv}\n操作总数 {ops_count}\n" \
|
||||||
"统计:\n{ops_list}\n{last_player} 你要对人家负责哟/// ///\n\n" \
|
"统计:\n{ops_list}\n{last_player} 你要对人家负责哟/// ///\n\n" \
|
||||||
"用时{time}秒,超时{timeouts}次\n\n" \
|
"用时{time}秒,超时{timeouts}次\n\n" \
|
||||||
|
"{last_player} {reward}\n\n" \
|
||||||
"/mine 开始新游戏"
|
"/mine 开始新游戏"
|
||||||
|
STEP_TEXT_TEMPLATE = "{last_player} 踩到了地雷!\n" \
|
||||||
|
"时间{time}秒,超时{timeouts}次\n\n" \
|
||||||
|
"{last_player} {reward}\n\n" \
|
||||||
|
"生命值:({remain}/{ttl})"
|
||||||
LOSE_TEXT_TEMPLATE = "一道火光之后,你就在天上飞了呢…好奇怪喵\n" \
|
LOSE_TEXT_TEMPLATE = "一道火光之后,你就在天上飞了呢…好奇怪喵\n" \
|
||||||
"地图:Op {s_op} / Is {s_is} / 3bv {s_3bv}\n操作总数 {ops_count}\n" \
|
"地图:Op {s_op} / Is {s_is} / 3bv {s_3bv}\n操作总数 {ops_count}\n" \
|
||||||
"统计:\n{ops_list}\n{last_player} 是我们中出的叛徒!\n\n" \
|
"统计:\n{ops_list}\n{last_player} 是我们中出的叛徒!\n\n" \
|
||||||
"用时{time}秒,超时{timeouts}次\n\n" \
|
"用时{time}秒,超时{timeouts}次\n\n" \
|
||||||
|
"{last_player} {reward}\n\n" \
|
||||||
"/mine 开始新游戏"
|
"/mine 开始新游戏"
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,12 +67,12 @@ def display_username(user, atuser=True, shorten=False, markdown=True):
|
||||||
return name
|
return name
|
||||||
|
|
||||||
class Game():
|
class Game():
|
||||||
def __init__(self, board, group, creator):
|
def __init__(self, board, group, creator, lives=1):
|
||||||
self.board = board
|
self.board = board
|
||||||
self.group = group
|
self.group = group
|
||||||
self.creator = creator
|
self.creator = creator
|
||||||
self.__actions = dict()
|
self.__actions = dict()
|
||||||
self.__last_player = None
|
self.last_player = None
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
# timestamp of the last update keyboard action,
|
# timestamp of the last update keyboard action,
|
||||||
|
@ -72,9 +81,11 @@ class Game():
|
||||||
self.last_action = 0
|
self.last_action = 0
|
||||||
# number of timeout error catched
|
# number of timeout error catched
|
||||||
self.timeouts = 0
|
self.timeouts = 0
|
||||||
|
self.lives = lives
|
||||||
|
self.ttl_lives = lives
|
||||||
def save_action(self, user, spot):
|
def save_action(self, user, spot):
|
||||||
'''spot is supposed to be a tuple'''
|
'''spot is supposed to be a tuple'''
|
||||||
self.__last_player = user
|
self.last_player = user
|
||||||
if self.__actions.get(user, None):
|
if self.__actions.get(user, None):
|
||||||
self.__actions[user].append(spot)
|
self.__actions[user].append(spot)
|
||||||
else:
|
else:
|
||||||
|
@ -82,11 +93,12 @@ class Game():
|
||||||
def actions_sum(self):
|
def actions_sum(self):
|
||||||
mysum = 0
|
mysum = 0
|
||||||
for user in self.__actions:
|
for user in self.__actions:
|
||||||
|
game_count(user)
|
||||||
count = len(self.__actions.get(user, list()))
|
count = len(self.__actions.get(user, list()))
|
||||||
mysum += count
|
mysum += count
|
||||||
return mysum
|
return mysum
|
||||||
def get_last_player(self):
|
def get_last_player(self):
|
||||||
return display_username(self.__last_player)
|
return display_username(self.last_player)
|
||||||
def get_actions(self):
|
def get_actions(self):
|
||||||
'''Convert actions into text'''
|
'''Convert actions into text'''
|
||||||
msg = ""
|
msg = ""
|
||||||
|
@ -98,7 +110,10 @@ class Game():
|
||||||
class GameManager:
|
class GameManager:
|
||||||
__games = dict()
|
__games = dict()
|
||||||
def append(self, board, board_hash, group_id, creator_id):
|
def append(self, board, board_hash, group_id, creator_id):
|
||||||
self.__games[board_hash] = Game(board, group_id, creator_id)
|
lives = int(board.mines/3)
|
||||||
|
if lives <= 0:
|
||||||
|
lives = 1
|
||||||
|
self.__games[board_hash] = Game(board, group_id, creator_id, lives=lives)
|
||||||
def remove(self, board_hash):
|
def remove(self, board_hash):
|
||||||
board = self.get_game_from_hash(board_hash)
|
board = self.get_game_from_hash(board_hash)
|
||||||
if board:
|
if board:
|
||||||
|
@ -114,10 +129,13 @@ class GameManager:
|
||||||
game_manager = GameManager()
|
game_manager = GameManager()
|
||||||
|
|
||||||
|
|
||||||
|
@run_async
|
||||||
def send_keyboard(bot, update, args):
|
def send_keyboard(bot, update, args):
|
||||||
msg = update.message
|
msg = update.message
|
||||||
logger.info("Mine from {0}".format(update.message.from_user.id))
|
logger.info("Mine from {0}".format(update.message.from_user.id))
|
||||||
|
if check_restriction(update.message.from_user):
|
||||||
|
update.message.reply_text("爆炸这么多次还想扫雷?")
|
||||||
|
return
|
||||||
# create a game board
|
# create a game board
|
||||||
if len(args) == 3:
|
if len(args) == 3:
|
||||||
height = HEIGHT
|
height = HEIGHT
|
||||||
|
@ -176,6 +194,69 @@ def send_status(bot, update):
|
||||||
count = game_manager.count()
|
count = game_manager.count()
|
||||||
update.message.reply_text('当前进行的游戏: {}'.format(count))
|
update.message.reply_text('当前进行的游戏: {}'.format(count))
|
||||||
|
|
||||||
|
def gen_reward(user, negative=True):
|
||||||
|
''' Reward the player :) '''
|
||||||
|
# Negative rewards
|
||||||
|
def restrict_mining(player):
|
||||||
|
if player.immunity_cards >= 1:
|
||||||
|
player.immunity_cards -= 1
|
||||||
|
ret = "用去一张免疫卡,还剩{}张".format(player.immunity_cards)
|
||||||
|
else:
|
||||||
|
now = int(time.time())
|
||||||
|
seconds = randint(30, 120)
|
||||||
|
player.restricted_until = now + seconds
|
||||||
|
ret = "被限制扫雷{}秒".format(seconds)
|
||||||
|
player.save()
|
||||||
|
return ret
|
||||||
|
# Positive rewards
|
||||||
|
def give_immunity_cards(player):
|
||||||
|
if player.immunity_cards >= 3 and choice((True, False)):
|
||||||
|
action = "没收"
|
||||||
|
player.immunity_cards -= 1
|
||||||
|
else:
|
||||||
|
action = "奖励"
|
||||||
|
player.immunity_cards += 1
|
||||||
|
player.save()
|
||||||
|
return "被{}了一张免疫卡,共有{}张".format(action, player.immunity_cards)
|
||||||
|
|
||||||
|
player = get_player(user.id)
|
||||||
|
if negative:
|
||||||
|
player.death += 1
|
||||||
|
return restrict_mining(player)
|
||||||
|
else:
|
||||||
|
player.wins += 1
|
||||||
|
return give_immunity_cards(player)
|
||||||
|
|
||||||
|
def game_count(user):
|
||||||
|
player = get_player(user.id)
|
||||||
|
player.mines += 1
|
||||||
|
player.save()
|
||||||
|
|
||||||
|
def check_restriction(user):
|
||||||
|
player = get_player(user.id)
|
||||||
|
player.db_close()
|
||||||
|
now = int(time.time())
|
||||||
|
if now >= player.restricted_until:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return player.restricted_until - now
|
||||||
|
|
||||||
|
@run_async
|
||||||
|
def player_statistics(bot, update):
|
||||||
|
logger.info("Statistics from {0}".format(update.message.from_user.id))
|
||||||
|
user = update.message.from_user
|
||||||
|
player = get_player(user.id)
|
||||||
|
player.db_close()
|
||||||
|
mines = player.mines
|
||||||
|
death = player.death
|
||||||
|
wins = player.wins
|
||||||
|
cards = player.immunity_cards
|
||||||
|
TEMPLATE = "一共玩了{mines}局,爆炸{death}次,赢了{wins}局\n" \
|
||||||
|
"口袋里有{cards}张免疫卡"
|
||||||
|
update.message.reply_text(TEMPLATE.format(mines=mines, death=death,
|
||||||
|
wins=wins, cards=cards))
|
||||||
|
|
||||||
|
|
||||||
def update_keyboard_request(bot, bhash, game, chat_id, message_id):
|
def update_keyboard_request(bot, bhash, game, chat_id, message_id):
|
||||||
current_action_timestamp = time.time()
|
current_action_timestamp = time.time()
|
||||||
if current_action_timestamp - game.last_action <= KBD_MIN_INTERVAL:
|
if current_action_timestamp - game.last_action <= KBD_MIN_INTERVAL:
|
||||||
|
@ -221,12 +302,18 @@ def update_keyboard(bot, job, noqueue=None):
|
||||||
logger.debug('time out in game {}.'.format(bhash))
|
logger.debug('time out in game {}.'.format(bhash))
|
||||||
game.timeouts += 1
|
game.timeouts += 1
|
||||||
|
|
||||||
|
@run_async
|
||||||
def handle_button_click(bot, update):
|
def handle_button_click(bot, update):
|
||||||
msg = update.callback_query.message
|
msg = update.callback_query.message
|
||||||
user = update.callback_query.from_user
|
user = update.callback_query.from_user
|
||||||
chat_id = update.callback_query.message.chat.id
|
chat_id = update.callback_query.message.chat.id
|
||||||
data = update.callback_query.data
|
data = update.callback_query.data
|
||||||
logger.debug('Button clicked by {}, data={}.'.format(user.id, data))
|
logger.debug('Button clicked by {}, data={}.'.format(user.id, data))
|
||||||
|
restriction = check_restriction(user)
|
||||||
|
if restriction:
|
||||||
|
bot.answer_callback_query(callback_query_id=update.callback_query.id,
|
||||||
|
text="还有{}秒才能扫雷".format(restriction), show_alert=True)
|
||||||
|
return
|
||||||
bot.answer_callback_query(callback_query_id=update.callback_query.id)
|
bot.answer_callback_query(callback_query_id=update.callback_query.id)
|
||||||
try:
|
try:
|
||||||
data = data.split(' ')
|
data = data.split(' ')
|
||||||
|
@ -262,18 +349,35 @@ def handle_button_click(bot, update):
|
||||||
last_player = game.get_last_player()
|
last_player = game.get_last_player()
|
||||||
time_used = time.time() - game.start_time
|
time_used = time.time() - game.start_time
|
||||||
timeouts = game.timeouts
|
timeouts = game.timeouts
|
||||||
|
remain = 0
|
||||||
|
ttl = 0
|
||||||
if board.state == 2:
|
if board.state == 2:
|
||||||
|
reward = gen_reward(game.last_player, negative=False)
|
||||||
template = WIN_TEXT_TEMPLATE
|
template = WIN_TEXT_TEMPLATE
|
||||||
|
elif board.state == 3:
|
||||||
|
reward = gen_reward(game.last_player, negative=True)
|
||||||
|
game.lives -= 1
|
||||||
|
if game.lives <= 0:
|
||||||
|
template = LOSE_TEXT_TEMPLATE
|
||||||
|
else:
|
||||||
|
game.stopped = False
|
||||||
|
board.state = 1
|
||||||
|
remain = game.lives
|
||||||
|
ttl = game.ttl_lives
|
||||||
|
template = STEP_TEXT_TEMPLATE
|
||||||
else:
|
else:
|
||||||
template = LOSE_TEXT_TEMPLATE
|
# Should not reach here
|
||||||
|
reward = None
|
||||||
myreply = template.format(s_op=s_op, s_is=s_is, s_3bv=s_3bv, ops_count=ops_count,
|
myreply = template.format(s_op=s_op, s_is=s_is, s_3bv=s_3bv, ops_count=ops_count,
|
||||||
ops_list=ops_list, last_player=last_player,
|
ops_list=ops_list, last_player=last_player,
|
||||||
time=round(time_used, 4), timeouts=timeouts)
|
time=round(time_used, 4), timeouts=timeouts, reward=reward,
|
||||||
|
remain=remain, ttl=ttl)
|
||||||
try:
|
try:
|
||||||
msg.reply_text(myreply, parse_mode="Markdown")
|
msg.reply_text(myreply, parse_mode="Markdown")
|
||||||
except TimedOutError:
|
except TimedOutError:
|
||||||
logger.debug('timeout sending report for game {}'.format(bhash))
|
logger.debug('timeout sending report for game {}'.format(bhash))
|
||||||
game_manager.remove(bhash)
|
if game.stopped:
|
||||||
|
game_manager.remove(bhash)
|
||||||
elif mmap is not None and (not array_equal(board.map, mmap)):
|
elif mmap is not None and (not array_equal(board.map, mmap)):
|
||||||
game.save_action(user, (row, col))
|
game.save_action(user, (row, col))
|
||||||
update_keyboard_request(bot, bhash, game, chat_id, msg.message_id)
|
update_keyboard_request(bot, bhash, game, chat_id, msg.message_id)
|
||||||
|
@ -283,6 +387,7 @@ def handle_button_click(bot, update):
|
||||||
updater.dispatcher.add_handler(CommandHandler('start', send_help))
|
updater.dispatcher.add_handler(CommandHandler('start', send_help))
|
||||||
updater.dispatcher.add_handler(CommandHandler('mine', send_keyboard, pass_args=True))
|
updater.dispatcher.add_handler(CommandHandler('mine', send_keyboard, pass_args=True))
|
||||||
updater.dispatcher.add_handler(CommandHandler('status', send_status))
|
updater.dispatcher.add_handler(CommandHandler('status', send_status))
|
||||||
|
updater.dispatcher.add_handler(CommandHandler('stats', player_statistics))
|
||||||
updater.dispatcher.add_handler(CommandHandler('source', send_source))
|
updater.dispatcher.add_handler(CommandHandler('source', send_source))
|
||||||
updater.dispatcher.add_handler(CallbackQueryHandler(handle_button_click))
|
updater.dispatcher.add_handler(CallbackQueryHandler(handle_button_click))
|
||||||
updater.start_polling()
|
updater.start_polling()
|
||||||
|
|
Loading…
Reference in a new issue