liars-dice/bot_ai.py

47 lines
1.7 KiB
Python
Raw Permalink Normal View History

2022-08-03 15:39:53 +00:00
import random
from scipy.stats import binom
from engine import LiarsDiceEngine, Bid
class BotAi:
def ai_turn(self, game_state: LiarsDiceEngine) -> Bid | None:
bid = game_state.current_bid or Bid(die_face=1, num_dice=0)
# Challenge a bid if we deem it sufficiently unlikely, based on the statistical odds of the bid being true. Do
# not hardcode a value to avoid the AI always reacting in a predetermined manner.
if (
self.calculate_odds(game_state, bid.die_face, bid.num_dice)
< random.uniform(0.1, 0.5)
):
return None
max_confidence = 0
max_confidence_bids = []
for d in range(bid.die_face, 7):
for n in range(1, game_state.num_all_dice):
if (d == bid.die_face and n > bid.num_dice) or d > bid.die_face:
confidence = self.calculate_odds(game_state, d, n)
if confidence == max_confidence:
max_confidence_bids.append(Bid(d, n))
elif confidence > max_confidence:
max_confidence = confidence
max_confidence_bids = [Bid(d, n)]
return random.choice(max_confidence_bids) if max_confidence_bids else None
@staticmethod
def calculate_odds(game_state: LiarsDiceEngine, die_face: int, num_values: int) -> float:
num_minimum_needed_values = (
num_values - game_state.dice[game_state.active_player_index][die_face] - 1
)
die_face_probability = 2.0 / 6 if game_state.wild_ones_variant else 1.0 / 6
return binom.sf(
k=num_minimum_needed_values,
n=game_state.num_opponent_dice,
p=die_face_probability,
)