package models import ( "errors" "fmt" "gralias/utils" "time" "github.com/rs/xid" ) type WordColor string const ( WordColorWhite = "amber" WordColorBlue = "blue" WordColorRed = "red" WordColorBlack = "black" WordColorUknown = "stone" // beige ) type ActionType string const ( ActionTypeClue = "gave_clue" ActionTypeGuess = "guessed" ActionTypeGameOver = "game_over" ActionTypeGameStarted = "game_started" ) func StrToWordColor(s string) WordColor { switch s { case "amber", "white": return WordColorWhite case "blue": return WordColorBlue case "red": return WordColorRed case "black": return WordColorBlack default: return WordColorUknown } } type Team struct { Guessers []string Mime string Color string } type Action struct { Actor string ActorColor string Action string // clue | guess Word string WordColor string Number string // for clue } type BotPlayer struct { Role UserRole // gueeser | mime Team UserTeam } type Room struct { ID string `json:"id" db:"id"` CreatedAt time.Time `json:"created_at" db:"created_at"` // limit? RoomPass string `json:"room_pass"` RoomLink string CreatorName string `json:"creator_name"` ActionHistory []Action TeamTurn UserTeam RedTeam Team BlueTeam Team Cards []WordCard ThisTurnLimit uint8 // how many cards guessers can open this turn OpenedThisTurn uint8 // how many cards have been opened this turn WCMap map[string]WordColor BotMap map[string]BotPlayer // key is bot name Result uint8 // 0 for unknown; 1 is win for red; 2 if for blue; BlueCounter uint8 RedCounter uint8 RedTurn bool // false is blue turn MimeDone bool IsPublic bool IsRunning bool `json:"is_running"` Language string `json:"language" example:"en" form:"language"` RoundTime int32 `json:"round_time"` IsOver bool TeamWon UserTeam // blue | red // needed for debug LogJournal []string LastActionTS time.Time } // FindBotByTeamRole returns bot name if found; otherwise empty string func (r *Room) FindBotByTeamRole(team, role string) string { for bn, b := range r.BotMap { if b.Role == StrToUserRole(role) && b.Team == StrToUserTeam(team) { return bn } } return "" } func (r *Room) FetchLastClue() (*Action, error) { for i := len(r.ActionHistory) - 1; i >= 0; i-- { if r.ActionHistory[i].Action == string(ActionTypeClue) { return &r.ActionHistory[i], nil } } return nil, errors.New("no clue in history") } func (r *Room) GetPlayerByName(name string) (role UserRole, team UserTeam, found bool) { if r.RedTeam.Mime == name { return "mime", "red", true } if r.BlueTeam.Mime == name { return "mime", "blue", true } for _, guesser := range r.RedTeam.Guessers { if guesser == name { return "guesser", "red", true } } for _, guesser := range r.BlueTeam.Guessers { if guesser == name { return "guesser", "blue", true } } return "", "", false } func (r *Room) CanStart() error { if r.IsRunning { return errors.New("cannot start; game is already running") } if r.RedTeam.Mime == "" { return errors.New("cannot start; red team has no mime") } if r.BlueTeam.Mime == "" { return errors.New("cannot start; blue team has no mime") } if len(r.RedTeam.Guessers) == 0 { return errors.New("cannot start; red team has no guessers") } if len(r.BlueTeam.Guessers) == 0 { return errors.New("cannot start; blue team has no guessers") } return nil } func getGuesser(m map[string]BotPlayer, team UserTeam) string { for k, v := range m { if v.Team == team && v.Role == UserRoleGuesser { return k } } return "" } // WhichBotToMove returns bot name that have to move or empty string func (r *Room) WhichBotToMove() string { fmt.Println("looking for bot to move", "team-turn:", r.TeamTurn, "mime-done:", r.MimeDone, "bot-map:", r.BotMap, "is_running:", r.IsRunning, "blueMime:", r.BlueTeam.Mime, "redMime:", r.RedTeam.Mime) if !r.IsRunning { return "" } switch r.TeamTurn { case UserTeamBlue: if !r.MimeDone { _, ok := r.BotMap[r.BlueTeam.Mime] if ok { return r.BlueTeam.Mime } } else { return getGuesser(r.BotMap, UserTeamBlue) } case UserTeamRed: if !r.MimeDone { _, ok := r.BotMap[r.RedTeam.Mime] if ok { return r.RedTeam.Mime } } else { return getGuesser(r.BotMap, UserTeamRed) } default: // how did we got here? return "" } return "" } func (r *Room) GetOppositeTeamColor() UserTeam { switch r.TeamTurn { case "red": return UserTeamBlue case "blue": return UserTeamRed } return UserTeamNone } func (r *Room) UpdateCounter() { redCounter := uint8(0) blueCounter := uint8(0) for _, card := range r.Cards { if card.Revealed { continue } switch card.Color { case WordColorRed: redCounter++ case WordColorBlue: blueCounter++ } } r.RedCounter = redCounter r.BlueCounter = blueCounter } func (r *Room) ChangeTurn() { switch r.TeamTurn { case "blue": r.TeamTurn = "red" case "red": r.TeamTurn = "blue" default: r.TeamTurn = "blue" } } func (r *Room) RevealAllCards() { for i := range r.Cards { r.Cards[i].Revealed = true } } func (r *Room) RevealSpecificWord(word string) { for i, card := range r.Cards { if card.Word == word { r.Cards[i].Revealed = true } } } type WordCard struct { Word string `json:"word"` Color WordColor `json:"color"` Revealed bool `json:"revealed"` } type GameSettings struct { IsRunning bool `json:"is_running"` Language string `json:"language" example:"en" form:"language"` RoundTime int32 `json:"round_time"` ProgressPct uint32 `json:"progress_pct"` IsOver bool } // ===== type RoomReq struct { // is not user or not unique RoomPass string `json:"room_pass" form:"room_pass"` RoomName string `json:"room_name" form:"room_name"` // GameSettings } func (rr *RoomReq) CreateRoom(creator string) *Room { roomID := xid.New().String() return &Room{ // RoomName: , RoomPass: rr.RoomPass, ID: roomID, CreatedAt: time.Now(), // PlayerList: []string{creator}, CreatorName: creator, BotMap: make(map[string]BotPlayer), } } // ==== type FullInfo struct { State *UserState Room *Room List []*Room LinkLogin string // room_id } func (f *FullInfo) ExitRoom() *Room { // f.Room.PlayerList = utils.RemoveFromSlice(f.State.Username, f.Room.PlayerList) f.Room.RedTeam.Guessers = utils.RemoveFromSlice(f.State.Username, f.Room.RedTeam.Guessers) f.Room.BlueTeam.Guessers = utils.RemoveFromSlice(f.State.Username, f.Room.BlueTeam.Guessers) if f.Room.RedTeam.Mime == f.State.Username { f.Room.RedTeam.Mime = "" } if f.Room.BlueTeam.Mime == f.State.Username { f.Room.BlueTeam.Mime = "" } f.State.ExitRoom() resp := f.Room f.Room = nil return resp }