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, | |
}; | |
} | |
} | |