Spaces:
Sleeping
Sleeping
| import { v, Infer, ObjectType } from 'convex/values'; | |
| import { Game } from './game'; | |
| import { | |
| DAY_DURATION, | |
| NIGHT_DURATION, | |
| WWOLF_VOTE_DURATION, | |
| PLAYER_KILL_VOTE_DURATION, | |
| } from '../constants'; | |
| import { processVotes } from './voting'; | |
| import { parseLLMVotingResult } from './voting'; | |
| import { LLmvotingCallWerewolf } from './voting'; | |
| import { GameId } from './ids'; | |
| import { Player } from './player'; | |
| export type CycleState = 'Day' | 'WerewolfVoting' | 'Night' | 'PlayerKillVoting' | 'EndGame' | 'LobbyState' | |
| const stateDurations: { [key in CycleState]: number } = { | |
| Day: DAY_DURATION, | |
| Night: NIGHT_DURATION, | |
| WerewolfVoting: WWOLF_VOTE_DURATION, | |
| PlayerKillVoting: PLAYER_KILL_VOTE_DURATION, | |
| EndGame: Infinity, | |
| LobbyState: Infinity | |
| }; | |
| const normalCycle: CycleState[] = [ | |
| 'Day', | |
| 'WerewolfVoting', | |
| 'Night', | |
| 'PlayerKillVoting', | |
| ]; | |
| const pushToGist = (averageCorrectVotes: number[]) => { | |
| const token = process.env.GITHUB_TOKEN; // Read GitHub token from environment variables | |
| const data = { | |
| description: "Average Correct Votes", | |
| public: true, | |
| files: { | |
| "averageCorrectVotes.json": { | |
| content: JSON.stringify(averageCorrectVotes) | |
| } | |
| } | |
| }; | |
| const headers = { | |
| "Accept": "application/vnd.github+json", | |
| "Authorization": `Bearer ${token}`, | |
| "X-GitHub-Api-Version": "2022-11-28" | |
| }; | |
| fetch('https://api.github.com/gists', { | |
| method: 'POST', | |
| headers: headers, | |
| body: JSON.stringify(data) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => console.log('Gist created:', data.html_url)) | |
| .catch(error => console.error('Error creating Gist:', error)); | |
| } | |
| const getCorrectVotesPercentage = (game: Game, playerId: GameId<'players'>, llms: Player[] ) => { | |
| const playerVotes = game.world.llmVotes.filter((vote) => vote.voter === playerId); | |
| if (playerVotes.length === 0) { | |
| return 0; | |
| } | |
| const correctVotes = playerVotes[0].playerIds.filter((id) => llms.map((llm) => llm.id).includes(id)); | |
| return correctVotes.length / llms.length; | |
| } | |
| export const gameCycleSchema = { | |
| currentTime: v.number(), | |
| cycleState: v.union( | |
| v.literal('Day'), | |
| v.literal('Night'), | |
| v.literal('WerewolfVoting'), | |
| v.literal('PlayerKillVoting'), | |
| v.literal('EndGame'), | |
| v.literal('LobbyState'), | |
| ), | |
| cycleIndex: v.number(), | |
| cycleNumber: v.number(), | |
| }; | |
| export type SerializedGameCycle = ObjectType<typeof gameCycleSchema>; | |
| const onStateChange = (prevState: CycleState, newState: CycleState, game: Game, now: number) => { | |
| console.log(`state changed: ${ prevState } -> ${ newState }`); | |
| console.log("newState is :",newState) | |
| if(newState ==="WerewolfVoting"){ | |
| const allVillagers = [...game.world.players.values()] | |
| const villagers = [...game.playerDescriptions.values()].filter(player => | |
| player.type === 'villager' | |
| ); | |
| // TODO: You should't vote for yourelf | |
| allVillagers.forEach((villager) => { | |
| LLmvotingCallWerewolf(villager, villagers).then(result => { | |
| parseLLMVotingResult(villager, result, game) | |
| }) | |
| }) | |
| }; | |
| if(newState ==="PlayerKillVoting"){ | |
| const werewolves = [...game.world.players.values()].filter((were) => { | |
| game.playerDescriptions.get(were.id)?.type === 'werewolf' | |
| }) | |
| const villagers = [...game.playerDescriptions.values()] | |
| werewolves.forEach((were) => { | |
| LLmvotingCallWerewolf(were, villagers).then(result => { | |
| parseLLMVotingResult(were, result, game) | |
| }) | |
| }) | |
| }; | |
| if (prevState === 'PlayerKillVoting') { | |
| // const werewolves = [...game.world.players.values()].filter((were) => { | |
| // game.playerDescriptions.get(were.id)?.type === 'werewolf' | |
| // }) | |
| // if (werewolves.length != 0) { | |
| const mostVotedPlayer = processVotes(game.world.gameVotes, [...game.world.players.values()])[0]; | |
| const playerToKill = game.world.players.get(mostVotedPlayer.playerId); | |
| console.log(`killing: ${playerToKill?.id}, with ${game.world.gameVotes.length} votes`) | |
| if (playerToKill) { | |
| playerToKill.kill(game, now); | |
| } | |
| // } else { | |
| // console.log('no werewolves, nobody was killed') | |
| // } | |
| game.world.gameVotes = []; | |
| } | |
| if (prevState === 'WerewolfVoting') { | |
| const mostVotedPlayer = processVotes(game.world.gameVotes, [...game.world.players.values()])[0]; | |
| const suspect = game.world.players.get(mostVotedPlayer.playerId); | |
| console.log(`suspect: ${suspect?.id}, with ${game.world.gameVotes.length} votes`) | |
| if (suspect?.playerType(game) === 'werewolf') { | |
| suspect?.kill(game, now) | |
| } | |
| game.world.gameVotes = []; | |
| } | |
| if (newState === 'EndGame') { | |
| const llms = [...game.world.playersInit.values()].filter((player) => { | |
| !player.human | |
| }) | |
| const averageCorrectVotes = game.world.llmVotes.map((votes) => { | |
| return getCorrectVotesPercentage(game, votes.voter, llms); | |
| }) | |
| // Push to gist | |
| pushToGist(averageCorrectVotes); | |
| } | |
| if (prevState == 'LobbyState') { | |
| game.assignRoles() | |
| } | |
| }; | |
| export class GameCycle { | |
| currentTime: number; | |
| cycleState: CycleState; | |
| cycleIndex: number; | |
| cycleNumber:number; | |
| constructor(serialized: SerializedGameCycle) { | |
| const { currentTime, cycleState, cycleIndex,cycleNumber} = serialized; | |
| this.currentTime = currentTime; | |
| this.cycleState = cycleState; | |
| this.cycleIndex = cycleIndex; | |
| this.cycleNumber=cycleNumber; | |
| } | |
| startGame(game: Game) { | |
| this.currentTime = 0; | |
| onStateChange(this.cycleState, 'Day', game, 0); | |
| this.cycleState = 'Day'; | |
| this.cycleIndex = 0; | |
| this.cycleNumber=0; | |
| console.log('Game started') | |
| } | |
| endgame(game: Game) { | |
| this.currentTime = 0; | |
| onStateChange(this.cycleState, 'EndGame', game, 0); | |
| this.cycleState = 'EndGame'; | |
| this.cycleIndex = -1; | |
| console.log('EndGame reached') | |
| } | |
| // Tick method to increment the counter | |
| tick(game: Game, tickDuration: number) { | |
| console.log(process.env.GITHUB_TOKEN) | |
| this.currentTime += tickDuration; | |
| if (this.currentTime >= stateDurations[this.cycleState]) { | |
| const prevState = this.cycleState; | |
| this.currentTime = 0; | |
| this.cycleIndex = (this.cycleIndex + 1) % normalCycle.length; | |
| this.cycleNumber = Math. ceil((this.cycleNumber + 1) / normalCycle.length); | |
| this.cycleState = normalCycle[this.cycleIndex]; | |
| onStateChange(prevState, this.cycleState, game, tickDuration); | |
| } | |
| } | |
| serialize(): SerializedGameCycle { | |
| const { currentTime, cycleState, cycleIndex , cycleNumber} = this; | |
| return { | |
| currentTime, | |
| cycleState, | |
| cycleIndex, | |
| cycleNumber, | |
| }; | |
| } | |
| } | |