dixit_bench / app.py
Elfsong's picture
Update
973277c
# coding: utf-8
# Author: Du Mingzhe (mingzhe@nus.edu.sg)
# Date: 2025-09-11
import os
import streamlit as st
from pathlib import Path
from typing import Tuple, List, Any
from dixit import Player, OpenAIPlayer, Game, Card
class OnlinePlayer(Player):
def __init__(self, player_id: str):
super().__init__(player_id)
def storytell(self, card: Card) -> str:
st.write("You are the Storyteller. Please tell a story about the card...")
st.image(str(card.image_path.resolve()), width=100)
return st.text_input("Story", "")
def guess(self, story: str, cards: List[Card]) -> Card:
if not st.session_state.guesser_status["player_human"]:
st.write("You are the Guesser. Please guess a card...")
st.success(f"Story: {story}")
image_col = st.columns(6)
for index, (card, image_col) in enumerate(zip(cards, image_col)):
with image_col:
st.image(str(card.image_path.resolve()), caption=f"Card {index}")
card_index = st.selectbox("GuessCard", [index for index in range(len(cards))], on_change=None)
if st.button("Guess"):
return cards[card_index]
return None
def vote(self, story: str, cards: List[Card]) -> Card:
if not st.session_state.vote_status["player_human"]:
st.write("You are the Guesser. Please vote a card...")
st.success(f"Story: {story}")
card_index = st.selectbox("VoteCard", [index for index in range(len(cards))], on_change=None)
if st.button("Vote"):
cards[card_index].voted_by.append(self.player_id)
return cards[card_index]
return None
def storytell_inference(self, card: Card) -> Tuple[str, str, float]:
return (None, None, 0.0)
def guess_inference(self, story: str, cards: List[Card]) -> Tuple[Card, str, float]:
return (None, None, 0.0)
def vote_inference(self, story: str, cards: List[Card]) -> Tuple[Card, str, float]:
return (None, None, 0.0)
st.title("Dixit :blue[Bench]")
if "stage" not in st.session_state:
token = st.text_input("OpenAI Token", type="password")
if token:
os.environ["OPENAI_API_KEY"] = token
st.session_state.stage = "initialization"
else:
st.error("Please enter your OpenAI Token")
st.stop()
# Initialization
if st.session_state.stage == "initialization":
# Initialize Players
st.session_state.player_list = [
OpenAIPlayer(player_id="player_1", model_name="gpt-4o-mini"),
OpenAIPlayer(player_id="player_2", model_name="gpt-5"),
OpenAIPlayer(player_id="player_3", model_name="gpt-5-mini"),
OpenAIPlayer(player_id="player_4", model_name="gpt-5-nano"),
OpenAIPlayer(player_id="player_5", model_name="gpt-4.1"),
OnlinePlayer(player_id="player_human"),
]
# Initialize Game
st.session_state.game = Game(st.session_state.player_list, Path('asset/cards'))
# Role Assignment
st.session_state.game.role_assignment()
st.session_state.human_player_role = "storyteller" if st.session_state.game.storyteller.player_id == "player_human" else "guesser"
st.session_state.guesser_status = {guesser.player_id: False for guesser in st.session_state.game.guesser_list}
st.session_state.vote_status = {guesser.player_id: False for guesser in st.session_state.game.guesser_list}
# Dealing
st.session_state.storyteller_card, st.session_state.guesser_card_dict = st.session_state.game.dealing()
# Update Stage
st.session_state.stage = "storytelling"
st.rerun()
# Context
st.write(f"You are a *{st.session_state.human_player_role}* in this round.")
# Storytelling
if st.session_state.stage == "storytelling":
st.write("## Storytelling")
with st.spinner("Storyteller is thinking... (it may take 1 minute)", show_time=True):
st.session_state.story = st.session_state.game.storyteller.storytell(st.session_state.storyteller_card)
if st.session_state.story != "":
st.success(f"Story: {st.session_state.story}")
st.session_state.game.storyteller.selected_card_id = st.session_state.storyteller_card.card_id
st.session_state.stage = "guessing"
st.rerun()
# Guessing
if st.session_state.stage == "guessing":
st.write("## Guessing")
for guesser in st.session_state.game.guesser_list:
if not st.session_state.guesser_status[guesser.player_id]:
with st.spinner(f"Guesser {guesser.player_id} is guessing...", show_time=True):
guessed_card = guesser.guess(st.session_state.story, st.session_state.guesser_card_dict[guesser.player_id])
if guessed_card:
st.session_state.guesser_status[guesser.player_id] = True
guessed_card.guessed_by = guesser.player_id
guesser.guessed_card_id = guessed_card.card_id
st.session_state.game.table_cards[guessed_card.card_id] = guessed_card
st.rerun()
if all(st.session_state.guesser_status.values()):
st.session_state.game.table_cards[st.session_state.storyteller_card.card_id] = st.session_state.storyteller_card
st.session_state.stage = "voting"
st.rerun()
# Voting
if st.session_state.stage == "voting":
st.write("## Voting")
image_col = st.columns(6)
for index, (card, image_col) in enumerate(zip(st.session_state.game.table_cards.values(), image_col)):
with image_col:
st.image(str(card.image_path.resolve()), caption=f"Card {index}")
for guesser in st.session_state.game.guesser_list:
if not st.session_state.vote_status[guesser.player_id]:
with st.spinner(f"Guesser {guesser.player_id} is voting...", show_time=True):
voted_card = guesser.vote(st.session_state.story, [st.session_state.game.table_cards[card_id] for card_id in st.session_state.game.table_cards])
if voted_card:
st.session_state.vote_status[guesser.player_id] = True
guesser.voted_card_id = voted_card.card_id
st.session_state.game.table_cards[voted_card.card_id] = voted_card
st.rerun()
if all(st.session_state.vote_status.values()):
st.session_state.stage = "scoring"
st.rerun()
# Scoring
if st.session_state.stage == "scoring":
st.write("## Scoring")
st.session_state.game.calculate_score()
st.write("Storytelling")
st.image(str(st.session_state.storyteller_card.image_path.resolve()), caption=f"Story: {st.session_state.story}", width=200)
st.write("Guessing")
for guesser in st.session_state.game.guesser_list:
cards = st.session_state.guesser_card_dict[guesser.player_id]
image_col = st.columns(6)
for index, (card, image_col) in enumerate(zip(cards, image_col)):
with image_col:
st.image(str(card.image_path.resolve()), caption=f"Card {index}")
st.write("Voting")
image_col = st.columns(6)
for index, (card, image_col) in enumerate(zip(st.session_state.game.table_cards.values(), image_col)):
with image_col:
st.image(str(card.image_path.resolve()), caption=f"Card {index}")
# Game Details
game_details = dict()
game_details["game_id"] = st.session_state.game.game_id
game_details["storyteller"] = st.session_state.game.storyteller.player_id
game_details["guessers"] = [guesser.player_id for guesser in st.session_state.game.guesser_list]
game_details["guess_details"] = dict()
game_details["vote_details"] = dict()
game_details["score"] = st.session_state.game.score_dict
# Guess Details
for card in st.session_state.game.table_cards.values():
game_details["guess_details"][card.card_id] = card.guessed_by
# Voted Details
for card in st.session_state.game.table_cards.values():
game_details["vote_details"][card.card_id] = card.voted_by
st.write(game_details)
st.success("Game Finished!")