Compare commits
5 Commits
febdfaf355
...
main
Author | SHA1 | Date | |
---|---|---|---|
3525ea42e1 | |||
f1db0f62fc | |||
fd5f5a7e7a | |||
fc2fe0a396 | |||
292db8133d |
@@ -6,7 +6,7 @@
|
||||
|
||||
FROM python:3.10-alpine3.16 AS build
|
||||
|
||||
RUN pip install --no-cache-dir discord peewee beautifultable
|
||||
RUN pip install --no-cache-dir discord peewee
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
@@ -15,4 +15,4 @@ FROM build AS run
|
||||
ENV discord_token ""
|
||||
ENV database_path "/tmp/baseball.db"
|
||||
|
||||
CMD ["python", "/app/main.py"]
|
||||
CMD ["python", "-u", "/app/main.py"]
|
1
Pipfile
1
Pipfile
@@ -8,7 +8,6 @@ discord = "*"
|
||||
peewee = "*"
|
||||
pylint = "*"
|
||||
black = "*"
|
||||
beautifultable = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
|
33
Pipfile.lock
generated
33
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "171e06b48e5905ea64caab54728c14bd65292a2957b33964c688f2172e677b62"
|
||||
"sha256": "b6fb0d9cfe588eab84db4024e6b319681e9e70d00c74282b6722fe283ac3e348"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -106,7 +106,7 @@
|
||||
"sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b",
|
||||
"sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"version": "==3.8.3"
|
||||
},
|
||||
"aiosignal": {
|
||||
@@ -130,7 +130,7 @@
|
||||
"sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15",
|
||||
"sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"attrs": {
|
||||
@@ -141,14 +141,6 @@
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==22.1.0"
|
||||
},
|
||||
"beautifultable": {
|
||||
"hashes": [
|
||||
"sha256:040bc987444965678f4ffdfa2f17ac357f1a13e5875b161c9a1899b375319d17",
|
||||
"sha256:b34053a581976c4c23064bb0e918a7f6f22273cf196c787946db323a6a2b7d84"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320",
|
||||
@@ -172,7 +164,7 @@
|
||||
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
|
||||
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"click": {
|
||||
@@ -333,7 +325,7 @@
|
||||
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"multidict": {
|
||||
@@ -425,11 +417,11 @@
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5",
|
||||
"sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"
|
||||
"sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6",
|
||||
"sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.10.2"
|
||||
"version": "==0.10.3"
|
||||
},
|
||||
"peewee": {
|
||||
"hashes": [
|
||||
@@ -467,16 +459,9 @@
|
||||
"sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b",
|
||||
"sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"version": "==0.11.6"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
|
||||
"sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
|
||||
],
|
||||
"version": "==0.2.5"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
"sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3",
|
||||
|
14
game/base.py
14
game/base.py
@@ -4,6 +4,8 @@
|
||||
|
||||
# pylint: disable=missing-module-docstring
|
||||
|
||||
import logging
|
||||
|
||||
from database.models import database, GameModel as Game
|
||||
|
||||
|
||||
@@ -24,6 +26,18 @@ class BaseGameManager:
|
||||
# Discord client instance
|
||||
self.discord = None
|
||||
|
||||
logger = logging.getLogger()
|
||||
console = logging.StreamHandler()
|
||||
|
||||
format_str = '%(asctime)s\t%(levelname)s -- %(processName)s %(filename)s:%(lineno)s -- %(message)s'
|
||||
console.setFormatter(logging.Formatter(format_str))
|
||||
|
||||
logger.addHandler(console)
|
||||
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
self.logger = logger
|
||||
|
||||
def __enter__(self):
|
||||
"""
|
||||
Allows use of `with Game() as game` for try/except statements
|
||||
|
30
game/clear.py
Normal file
30
game/clear.py
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2022 - c0de <c0de@c0de.dev>
|
||||
# Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
|
||||
# pylint: disable=missing-module-docstring,too-few-public-methods
|
||||
|
||||
from database.models import PlayerModel as Player
|
||||
from game.base import BaseGameManager
|
||||
|
||||
|
||||
class ClearManager(BaseGameManager):
|
||||
"""Commands that run when a player clears the session leaderboard"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.commands.append(("clear", self.clear))
|
||||
|
||||
async def clear(self):
|
||||
"""Clear command - Clears the session scoreboard"""
|
||||
|
||||
players = Player.select(Player.player_id, Player.total_points)
|
||||
|
||||
for player in players:
|
||||
player.total_points = 0
|
||||
|
||||
Player.bulk_update(players, fields=[Player.total_points])
|
||||
|
||||
clear_message = "The score has been cleared!"
|
||||
|
||||
await self.message.channel.send(clear_message)
|
@@ -6,49 +6,26 @@
|
||||
|
||||
import datetime
|
||||
|
||||
from beautifultable import BeautifulTable
|
||||
|
||||
from database.models import GameModel as Game, GuessModel as Guess
|
||||
from game.base import BaseGameManager
|
||||
from game.process_guess import ProcessGuess
|
||||
|
||||
|
||||
class EndGameManager(BaseGameManager):
|
||||
"""Commands that run at the end of a game"""
|
||||
"""Commands that run at the end of a play"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.commands.append(("resolve", self.stop))
|
||||
|
||||
def __stop_args__(self):
|
||||
pieces = self.message.content.split()
|
||||
|
||||
if len(pieces) == 2:
|
||||
return pieces[1], False, None, None
|
||||
|
||||
if len(pieces) == 4:
|
||||
return pieces[1], True, pieces[2], pieces[3]
|
||||
|
||||
return None, False, None, None
|
||||
|
||||
async def update_pitch_value(self):
|
||||
"""Update game state database for closing arguments"""
|
||||
# Determine arguments
|
||||
pitch_value, has_batter, batter_id, batter_guess = self.__stop_args__()
|
||||
pitch_value = self.message.content.split()[1]
|
||||
if not pitch_value:
|
||||
return await self.message.channel.send(
|
||||
f"Invalid command <@{ str(self.message.author.id) }>!"
|
||||
)
|
||||
|
||||
if has_batter:
|
||||
player_id = batter_id[3:]
|
||||
Guess.create(
|
||||
game_id=self.game.game_id,
|
||||
player_id=player_id,
|
||||
player_name=self.discord.get_user(int(player_id).name),
|
||||
guess=int(batter_guess),
|
||||
).save()
|
||||
|
||||
# Save the pitch value
|
||||
Game.update(
|
||||
{
|
||||
@@ -77,40 +54,33 @@ class EndGameManager(BaseGameManager):
|
||||
)
|
||||
|
||||
# Discard the game if there weren't enough players
|
||||
if guess_count < 3:
|
||||
if guess_count < 2:
|
||||
self.game = None
|
||||
self.is_running = False
|
||||
return await self.message.channel.send(
|
||||
("Play closed!\n" + "However, there were not enough participants.")
|
||||
)
|
||||
|
||||
message = "Closed this play! Here are the results:"
|
||||
message += "\n```\n"
|
||||
|
||||
table = BeautifulTable()
|
||||
table.column_headers = [
|
||||
"PLAYER",
|
||||
"GUESS",
|
||||
"DIFFERENCE",
|
||||
"POINTS GAINED",
|
||||
"TOTAL POINTS",
|
||||
]
|
||||
message = (
|
||||
"Closed this play! Here are the results\n"
|
||||
+ "__PLAYER | GUESS | DIFFERENCE | POINTS GAINED | TOTAL POINTS__\n"
|
||||
)
|
||||
|
||||
pitch_value = await self.update_pitch_value()
|
||||
guess_processor = ProcessGuess(
|
||||
game=self, pitch_value=pitch_value, message=table
|
||||
game=self, pitch_value=pitch_value, message=message
|
||||
)
|
||||
|
||||
(
|
||||
table,
|
||||
message,
|
||||
closest_player_id,
|
||||
furthest_player_id,
|
||||
) = guess_processor.process_guesses()
|
||||
|
||||
message += str(table)
|
||||
message += "\n```\n"
|
||||
message += f"Congrats <@{closest_player_id}>! You were the closest!\n"
|
||||
message += f"Sorry <@{furthest_player_id}>, you were way off\n"
|
||||
message += (
|
||||
f"\nCongrats <@{closest_player_id}>! You were the closest!\n"
|
||||
+ f"Sorry <@{furthest_player_id}>, you were way off"
|
||||
)
|
||||
|
||||
await self.message.channel.send(message)
|
||||
|
||||
|
@@ -24,10 +24,9 @@ class HelpManager(BaseGameManager):
|
||||
+ "!guess - While a game is running, add a guess"
|
||||
+ " (or update an existing one) from 1-1000\n"
|
||||
+ "!resolve <value> - 1-1000 to resolve the game\n"
|
||||
+ " You can also add a batter's guess with: "
|
||||
+ "!resolve <value> <discord id #> <guess>\n"
|
||||
+ "!clear - Clear the session scoreboard\n"
|
||||
+ "!points - Shows a table of the most recent players, and their scores\n"
|
||||
+ "!reset - Removes all the guesses"
|
||||
+ "!reset - Removes all players and total points\n"
|
||||
+ "!help - Shows this message"
|
||||
)
|
||||
|
||||
|
@@ -16,10 +16,11 @@ from game.guess import GuessManager
|
||||
from game.points import PointsManager
|
||||
from game.reset import ResetManager
|
||||
from game.help import HelpManager
|
||||
from game.clear import ClearManager
|
||||
|
||||
|
||||
class GameManager(
|
||||
NewGameManager, EndGameManager, GuessManager, PointsManager, ResetManager, HelpManager
|
||||
NewGameManager, EndGameManager, GuessManager, PointsManager, ResetManager, HelpManager, ClearManager
|
||||
):
|
||||
"""
|
||||
Represents what this bot is able to do on a channel (or DMs)
|
||||
|
@@ -13,7 +13,7 @@ class NewGameManager(BaseGameManager):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.commands.append(("braveball", self.start))
|
||||
self.commands.append(("bb", self.start))
|
||||
|
||||
async def start(self):
|
||||
"""
|
||||
|
@@ -4,14 +4,12 @@
|
||||
|
||||
# pylint: disable=not-an-iterable,missing-module-docstring,too-few-public-methods
|
||||
|
||||
from beautifultable import BeautifulTable
|
||||
|
||||
from database.models import PlayerModel as Player
|
||||
from game.base import BaseGameManager
|
||||
|
||||
|
||||
class PointsManager(BaseGameManager):
|
||||
"""Commands that run when a player makes a guess"""
|
||||
"""Commands that run when a player wants to view the session leaderboard"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -25,23 +23,22 @@ class PointsManager(BaseGameManager):
|
||||
message = (
|
||||
"\nPlayers, who played recently, with their points highest to lowest\n\n"
|
||||
)
|
||||
|
||||
message += "```\n"
|
||||
table = BeautifulTable()
|
||||
table.column_headers = ["Player", "Total Points", "Last Played"]
|
||||
message += "Player | Total Points | Last Played\n"
|
||||
|
||||
players = Player.select(
|
||||
Player.player_name, Player.total_points, Player.last_update
|
||||
).order_by(Player.last_update.desc(), Player.total_points.desc())
|
||||
).order_by(Player.total_points.desc())
|
||||
|
||||
for player in players:
|
||||
table.rows.append([
|
||||
player.player_name,
|
||||
str(player.total_points),
|
||||
str(player.last_update)[:-10],
|
||||
])
|
||||
|
||||
message += str(table)
|
||||
message += "\n```\n"
|
||||
message += (
|
||||
" | ".join(
|
||||
[
|
||||
player.player_name,
|
||||
str(player.total_points),
|
||||
str(player.last_update)[:-10],
|
||||
]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
return await self.message.channel.send(message)
|
||||
|
@@ -40,6 +40,7 @@ class ProcessGuess:
|
||||
Player.player_id,
|
||||
Player.player_name,
|
||||
Player.total_points,
|
||||
Guess.difference
|
||||
)
|
||||
.join(Player)
|
||||
.where(
|
||||
@@ -71,14 +72,25 @@ class ProcessGuess:
|
||||
|
||||
def get_difference(self, guess=None):
|
||||
"""Difference calculation, includes "loop over" effect"""
|
||||
self.game_manager.logger.debug("get_difference")
|
||||
|
||||
self.game_manager.logger.debug(guess if guess else "")
|
||||
|
||||
if not guess:
|
||||
guess = self.guess.guess
|
||||
|
||||
self.game_manager.logger.debug(f"guess: {guess}")
|
||||
|
||||
difference = abs(guess - self.pitch_value)
|
||||
|
||||
self.game_manager.logger.debug(f"Difference:{difference}")
|
||||
|
||||
if difference > 500:
|
||||
difference = 1000 - difference
|
||||
|
||||
self.game_manager.logger.debug("Diff loop over 500")
|
||||
self.game_manager.logger.debug(f"{difference}")
|
||||
|
||||
self.difference = difference
|
||||
return self.difference
|
||||
|
||||
@@ -88,32 +100,51 @@ class ProcessGuess:
|
||||
they are (within range of 0-500) to the pitch_value
|
||||
"""
|
||||
|
||||
self.game_manager.logger.debug("> get_difference_score")
|
||||
self.game_manager.logger.debug(self.difference)
|
||||
|
||||
if self.difference == 0:
|
||||
self.difference_score = 15
|
||||
self.game_manager.logger.debug("0 Diff")
|
||||
elif self.difference > 0 and self.difference < 21:
|
||||
self.difference_score = 8
|
||||
self.game_manager.logger.debug("0 to 20 diff")
|
||||
elif self.difference > 20 and self.difference < 51:
|
||||
self.difference_score = 5
|
||||
self.game_manager.logger.debug("21 to 50 Diff")
|
||||
elif self.difference > 50 and self.difference < 101:
|
||||
self.difference_score = 3
|
||||
self.game_manager.logger.debug("51 to 100 Diff")
|
||||
elif self.difference > 100 and self.difference < 151:
|
||||
self.difference_score = 2
|
||||
self.game_manager.logger.debug("101 to 150 Diff")
|
||||
elif self.difference > 150 and self.difference < 201:
|
||||
self.difference_score = 1
|
||||
self.game_manager.logger.debug("151 to 200 Diff")
|
||||
elif self.difference > 200 and self.difference < 495:
|
||||
self.difference_score = 0
|
||||
self.game_manager.logger.debug("Diff too big")
|
||||
else:
|
||||
self.difference_score = -5
|
||||
self.game_manager.logger.debug("Big succ")
|
||||
|
||||
return self.difference_score
|
||||
|
||||
def get_winner_loser(self):
|
||||
"""Determine which guesses are closest and furthest from the pitch_value"""
|
||||
|
||||
self.game_manager.logger.debug("> get_winner_loser")
|
||||
|
||||
guess_values = [record.guess for record in self.get_guesses()]
|
||||
self.game_manager.logger.debug(", ".join([str(guess) for guess in guess_values]))
|
||||
|
||||
# Closest to the pitch_value
|
||||
winner = min(guess_values, key=lambda guess: self.get_difference(guess))
|
||||
self.game_manager.logger.debug(f"winner: {winner}")
|
||||
|
||||
# Furthest from the pitch_value
|
||||
loser = max(guess_values, key=lambda guess: self.get_difference(guess))
|
||||
self.game_manager.logger.debug(f"loser: {loser}")
|
||||
|
||||
return winner, loser
|
||||
|
||||
@@ -129,20 +160,17 @@ class ProcessGuess:
|
||||
for guess in self.get_guesses():
|
||||
self.guess = guess
|
||||
|
||||
self.game_manager.logger.debug(f"Current guess: {guess}")
|
||||
|
||||
|
||||
difference = self.get_difference()
|
||||
difference_score = self.get_difference_score()
|
||||
self.update_difference_value()
|
||||
self.update_player_total_points()
|
||||
|
||||
self.message.rows.append(
|
||||
[
|
||||
guess.player.player_name,
|
||||
guess.guess,
|
||||
difference,
|
||||
difference_score,
|
||||
guess.player.total_points + difference_score,
|
||||
]
|
||||
)
|
||||
self.message += f"{guess.player.player_name} | {guess.guess} | {difference} | {difference_score} | {(guess.player.total_points + difference_score)}\n"
|
||||
|
||||
self.game_manager.logger.debug(f"new total: {(guess.player.total_points + difference_score)}")
|
||||
|
||||
if guess.guess == winner:
|
||||
closest_player_id = guess.player.player_id
|
||||
@@ -150,4 +178,5 @@ class ProcessGuess:
|
||||
if guess.guess == loser:
|
||||
furthest_player_id = guess.player.player_id
|
||||
|
||||
self.game_manager.logger.debug(self.message)
|
||||
return self.message, closest_player_id, furthest_player_id
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
# pylint: disable=missing-module-docstring,too-few-public-methods
|
||||
|
||||
from database.models import GuessModel as Guess
|
||||
from database.models import PlayerModel as Player
|
||||
from game.base import BaseGameManager
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ResetManager(BaseGameManager):
|
||||
self.commands.append(("reset", self.reset))
|
||||
|
||||
async def reset(self):
|
||||
"""Reset command purges all guesses"""
|
||||
Guess.delete().where(True).execute()
|
||||
"""Reset command purges all players (removes total points)"""
|
||||
Player.delete().where(True).execute()
|
||||
|
||||
return await self.message.channel.send("ok")
|
||||
|
Reference in New Issue
Block a user