Feat: guess limit
This commit is contained in:
		| @@ -14,6 +14,11 @@ | ||||
|     <p>GAME OVER; team <span class="text-{{.Room.TeamWon}}-500">{{.Room.TeamWon}}</span> won! 🧚</p> | ||||
|     {{else}} | ||||
|     <p>Turn of the <span class="text-{{.Room.TeamTurn}}-500">{{.Room.TeamTurn}}</span> team</p> | ||||
|       {{if .Room.MimeDone}} | ||||
|       <p class="text-{{.Room.TeamTurn}}-500">Waiting for guessers</p> | ||||
|       {{else}} | ||||
|       <p class="text-{{.Room.TeamTurn}}-500">Waiting for mime</p> | ||||
|       {{end}} | ||||
|     {{end}} | ||||
|     <p> | ||||
|       {{if eq .State.Team ""}} | ||||
|   | ||||
| @@ -112,18 +112,19 @@ func loadState(username string) (*models.UserState, error) { | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func loadBot(botName, roomID string) (*llmapi.Bot, error) { | ||||
| 	key := "botkey_" + roomID + botName | ||||
| 	data, err := memcache.Get(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp := &llmapi.Bot{} | ||||
| 	if err := json.Unmarshal(data, &resp); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
| // not used | ||||
| // func loadBot(botName, roomID string) (*llmapi.Bot, error) { | ||||
| // 	key := "botkey_" + roomID + botName | ||||
| // 	data, err := memcache.Get(key) | ||||
| // 	if err != nil { | ||||
| // 		return nil, err | ||||
| // 	} | ||||
| // 	resp := &llmapi.Bot{} | ||||
| // 	if err := json.Unmarshal(data, &resp); err != nil { | ||||
| // 		return nil, err | ||||
| // 	} | ||||
| // 	return resp, nil | ||||
| // } | ||||
|  | ||||
| func getAllNames() []string { | ||||
| 	names := []string{} | ||||
|   | ||||
| @@ -84,6 +84,14 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | ||||
| 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 	// if opened card is of color of opp team, change turn | ||||
| 	oppositeColor := fi.Room.GetOppositeTeamColor() | ||||
| 	fi.Room.OpenedThisTurn++ | ||||
| 	if fi.Room.ThisTurnLimit >= fi.Room.OpenedThisTurn { | ||||
| 		// end turn | ||||
| 		fi.Room.TeamTurn = oppositeColor | ||||
| 		fi.Room.MimeDone = false | ||||
| 		fi.Room.OpenedThisTurn = 0 | ||||
| 		fi.Room.ThisTurnLimit = 0 | ||||
| 	} | ||||
| 	switch string(color) { | ||||
| 	case "black": | ||||
| 		// game over | ||||
| @@ -96,38 +104,46 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | ||||
| 			WordColor:  "black", | ||||
| 			Action:     "game over", | ||||
| 		} | ||||
| 		fi.Room.OpenedThisTurn = 0 | ||||
| 		fi.Room.ThisTurnLimit = 0 | ||||
| 		fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 	case "white", string(oppositeColor): | ||||
| 		// end turn | ||||
| 		fi.Room.TeamTurn = oppositeColor | ||||
| 		fi.Room.MimeDone = false | ||||
| 	} | ||||
| 	// check if no cards left => game over | ||||
| 	if fi.Room.BlueCounter == 0 { | ||||
| 		// blue won | ||||
| 		fi.Room.IsRunning = false | ||||
| 		fi.Room.IsOver = true | ||||
| 		fi.Room.TeamWon = "blue" | ||||
| 		action := models.Action{ | ||||
| 			Actor:      fi.State.Username, | ||||
| 			ActorColor: string(fi.State.Team), | ||||
| 			WordColor:  "blue", | ||||
| 			Action:     "game over", | ||||
| 		fi.Room.OpenedThisTurn = 0 | ||||
| 		fi.Room.ThisTurnLimit = 0 | ||||
| 		// check if no cards left => game over | ||||
| 		if fi.Room.BlueCounter == 0 { | ||||
| 			// blue won | ||||
| 			fi.Room.IsRunning = false | ||||
| 			fi.Room.IsOver = true | ||||
| 			fi.Room.TeamWon = "blue" | ||||
| 			action := models.Action{ | ||||
| 				Actor:      fi.State.Username, | ||||
| 				ActorColor: string(fi.State.Team), | ||||
| 				WordColor:  "blue", | ||||
| 				Action:     "game over", | ||||
| 			} | ||||
| 			fi.Room.OpenedThisTurn = 0 | ||||
| 			fi.Room.ThisTurnLimit = 0 | ||||
| 			fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 		} | ||||
| 		fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 	} | ||||
| 	if fi.Room.RedCounter == 0 { | ||||
| 		// red won | ||||
| 		fi.Room.IsRunning = false | ||||
| 		fi.Room.IsOver = true | ||||
| 		fi.Room.TeamWon = "red" | ||||
| 		action := models.Action{ | ||||
| 			Actor:      fi.State.Username, | ||||
| 			ActorColor: string(fi.State.Team), | ||||
| 			WordColor:  "red", | ||||
| 			Action:     "game over", | ||||
| 		if fi.Room.RedCounter == 0 { | ||||
| 			// red won | ||||
| 			fi.Room.IsRunning = false | ||||
| 			fi.Room.IsOver = true | ||||
| 			fi.Room.TeamWon = "red" | ||||
| 			action := models.Action{ | ||||
| 				Actor:      fi.State.Username, | ||||
| 				ActorColor: string(fi.State.Team), | ||||
| 				WordColor:  "red", | ||||
| 				Action:     "game over", | ||||
| 			} | ||||
| 			fi.Room.OpenedThisTurn = 0 | ||||
| 			fi.Room.ThisTurnLimit = 0 | ||||
| 			fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 		} | ||||
| 		fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 	} | ||||
| 	if err := saveFullInfo(fi); err != nil { | ||||
| 		abortWithError(w, err.Error()) | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import ( | ||||
| 	"gralias/models" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { | ||||
| @@ -143,6 +144,8 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) { | ||||
| 	fi.Room.IsRunning = true | ||||
| 	fi.Room.IsOver = false | ||||
| 	fi.Room.TeamTurn = "blue" | ||||
| 	fi.Room.OpenedThisTurn = 0 | ||||
| 	fi.Room.ThisTurnLimit = 0 | ||||
| 	loadCards(fi.Room) | ||||
| 	fi.Room.UpdateCounter() | ||||
| 	fi.Room.TeamWon = "" | ||||
| @@ -226,6 +229,11 @@ func HandleGiveClue(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	guessLimitU64, err := strconv.ParseUint(num, 10, 8) | ||||
| 	if err != nil { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	// validations === | ||||
| 	if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) { | ||||
| 		err = errors.New("not your team's move") | ||||
| @@ -253,6 +261,7 @@ func HandleGiveClue(w http.ResponseWriter, r *http.Request) { | ||||
| 	} | ||||
| 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||
| 	fi.Room.MimeDone = true | ||||
| 	fi.Room.ThisTurnLimit = uint8(guessLimitU64) + 1 | ||||
| 	notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num) | ||||
| 	notifyBotIfNeeded(fi) | ||||
| 	if err := saveFullInfo(fi); err != nil { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package llmapi | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"log/slog" | ||||
| 	"strings" | ||||
| @@ -29,7 +30,7 @@ func (p *deepSeekParser) ParseBytes(body []byte) (map[string]any, error) { | ||||
| 	} | ||||
| 	if len(dsResp.Choices) == 0 { | ||||
| 		p.log.Error("empty choices", "dsResp", dsResp) | ||||
| 		err := fmt.Errorf("empty choices in dsResp") | ||||
| 		err := errors.New("empty choices in dsResp") | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	text := dsResp.Choices[0].Text | ||||
| @@ -37,7 +38,7 @@ func (p *deepSeekParser) ParseBytes(body []byte) (map[string]any, error) { | ||||
| 	ri := strings.LastIndex(text, "}") | ||||
| 	if li < 0 || ri < 1 { | ||||
| 		p.log.Error("not a json", "msg", text) | ||||
| 		err := fmt.Errorf("fn: ParseBytes, not a json") | ||||
| 		err := fmt.Errorf("fn: ParseBytes, not a json; data: %s", text) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	sj := text[li : ri+1] | ||||
| @@ -67,7 +68,7 @@ func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) { | ||||
| 	} | ||||
| 	if len(resp.Choices) == 0 { | ||||
| 		p.log.Error("empty choices", "resp", resp) | ||||
| 		err := fmt.Errorf("empty choices in resp") | ||||
| 		err := errors.New("empty choices in resp") | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	text := resp.Choices[0].Message.Content | ||||
| @@ -75,7 +76,7 @@ func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) { | ||||
| 	ri := strings.LastIndex(text, "}") | ||||
| 	if li < 0 || ri < 1 { | ||||
| 		p.log.Error("not a json", "msg", text) | ||||
| 		err := fmt.Errorf("fn: ParseBytes, not a json") | ||||
| 		err := fmt.Errorf("fn: ParseBytes, not a json; data: %s", text) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	sj := text[li : ri+1] | ||||
|   | ||||
							
								
								
									
										3
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								main.go
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"gralias/config" | ||||
| 	"gralias/handlers" | ||||
| 	"log/slog" | ||||
| @@ -19,7 +18,7 @@ func ListenToRequests(port string) error { | ||||
| 	mux := http.NewServeMux() | ||||
| 	server := &http.Server{ | ||||
| 		Handler:      handlers.LogRequests(handlers.GetSession(mux)), | ||||
| 		Addr:         fmt.Sprintf(":%s", port), | ||||
| 		Addr:         ":" + port, | ||||
| 		ReadTimeout:  time.Second * 5, // TODO: to cfg | ||||
| 		WriteTimeout: 0,               // sse streaming | ||||
| 	} | ||||
|   | ||||
| @@ -58,23 +58,25 @@ type Room struct { | ||||
| 	ID        string    `json:"id" db:"id"` | ||||
| 	CreatedAt time.Time `json:"created_at" db:"created_at"` | ||||
| 	// RoomName     string    `json:"room_name"` | ||||
| 	RoomPass      string `json:"room_pass"` | ||||
| 	RoomLink      string | ||||
| 	CreatorName   string   `json:"creator_name"` | ||||
| 	PlayerList    []string `json:"player_list"` | ||||
| 	ActionHistory []Action | ||||
| 	TeamTurn      UserTeam | ||||
| 	RedTeam       Team | ||||
| 	BlueTeam      Team | ||||
| 	Cards         []WordCard | ||||
| 	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 | ||||
| 	RoomPass       string `json:"room_pass"` | ||||
| 	RoomLink       string | ||||
| 	CreatorName    string   `json:"creator_name"` | ||||
| 	PlayerList     []string `json:"player_list"` | ||||
| 	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 | ||||
| 	// GameSettings *GameSettings `json:"settings"` | ||||
| 	IsRunning bool   `json:"is_running"` | ||||
| 	Language  string `json:"language" example:"en" form:"language"` | ||||
|   | ||||
							
								
								
									
										8
									
								
								todos.md
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								todos.md
									
									
									
									
									
								
							| @@ -6,10 +6,9 @@ | ||||
| - invite link; + | ||||
| - login with invite link; + | ||||
| - add html icons of whos turn it is (like an image of big ? when mime is thinking); | ||||
| - there three places for bot to check if its its move: start-game; end-turn, after mime gave clue; | ||||
| - remove bot button (if game is not running, or bot already added); | ||||
| - there three places for bot to check if its its move: start-game; end-turn, after mime gave clue; + | ||||
| - remove bot button (if game is not running, or bot already added); + | ||||
| - show in backlog (and with that in prompt to llm) how many cards are left to open, also additional comment: if guess was right; | ||||
| - if bot already added; remove add bot button; | ||||
| - hide clue input for mime when it's not their turn; | ||||
| - needs resend to llm btn; + | ||||
|  | ||||
| @@ -26,7 +25,8 @@ | ||||
| ### issues | ||||
| - after the game started (isrunning) players should be able join guessers, but not switch team, or join as a mime; | ||||
| - cleanup backlog after new game is started; | ||||
| - guessers should not be able to open more cards, than mime gave them +1; | ||||
| - guessers should not be able to open more cards, than mime gave them +1 (auto end turn); + | ||||
| - 0 should mean without limit; | ||||
| - sse hangs / fails connection which causes to wait for cards to open a few seconds (on local machine); | ||||
| - after starting a new game (after old one) blue mime has no clue input; | ||||
| - gameover to backlog; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder