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> |     <p>GAME OVER; team <span class="text-{{.Room.TeamWon}}-500">{{.Room.TeamWon}}</span> won! 🧚</p> | ||||||
|     {{else}} |     {{else}} | ||||||
|     <p>Turn of the <span class="text-{{.Room.TeamTurn}}-500">{{.Room.TeamTurn}}</span> team</p> |     <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}} |     {{end}} | ||||||
|     <p> |     <p> | ||||||
|       {{if eq .State.Team ""}} |       {{if eq .State.Team ""}} | ||||||
|   | |||||||
| @@ -112,18 +112,19 @@ func loadState(username string) (*models.UserState, error) { | |||||||
| 	return resp, nil | 	return resp, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func loadBot(botName, roomID string) (*llmapi.Bot, error) { | // not used | ||||||
| 	key := "botkey_" + roomID + botName | // func loadBot(botName, roomID string) (*llmapi.Bot, error) { | ||||||
| 	data, err := memcache.Get(key) | // 	key := "botkey_" + roomID + botName | ||||||
| 	if err != nil { | // 	data, err := memcache.Get(key) | ||||||
| 		return nil, err | // 	if err != nil { | ||||||
| 	} | // 		return nil, err | ||||||
| 	resp := &llmapi.Bot{} | // 	} | ||||||
| 	if err := json.Unmarshal(data, &resp); err != nil { | // 	resp := &llmapi.Bot{} | ||||||
| 		return nil, err | // 	if err := json.Unmarshal(data, &resp); err != nil { | ||||||
| 	} | // 		return nil, err | ||||||
| 	return resp, nil | // 	} | ||||||
| } | // 	return resp, nil | ||||||
|  | // } | ||||||
|  |  | ||||||
| func getAllNames() []string { | func getAllNames() []string { | ||||||
| 	names := []string{} | 	names := []string{} | ||||||
|   | |||||||
| @@ -84,6 +84,14 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | |||||||
| 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||||
| 	// if opened card is of color of opp team, change turn | 	// if opened card is of color of opp team, change turn | ||||||
| 	oppositeColor := fi.Room.GetOppositeTeamColor() | 	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) { | 	switch string(color) { | ||||||
| 	case "black": | 	case "black": | ||||||
| 		// game over | 		// game over | ||||||
| @@ -96,12 +104,15 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | |||||||
| 			WordColor:  "black", | 			WordColor:  "black", | ||||||
| 			Action:     "game over", | 			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) | ||||||
| 	case "white", string(oppositeColor): | 	case "white", string(oppositeColor): | ||||||
| 		// end turn | 		// end turn | ||||||
| 		fi.Room.TeamTurn = oppositeColor | 		fi.Room.TeamTurn = oppositeColor | ||||||
| 		fi.Room.MimeDone = false | 		fi.Room.MimeDone = false | ||||||
| 	} | 		fi.Room.OpenedThisTurn = 0 | ||||||
|  | 		fi.Room.ThisTurnLimit = 0 | ||||||
| 		// check if no cards left => game over | 		// check if no cards left => game over | ||||||
| 		if fi.Room.BlueCounter == 0 { | 		if fi.Room.BlueCounter == 0 { | ||||||
| 			// blue won | 			// blue won | ||||||
| @@ -114,6 +125,8 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | |||||||
| 				WordColor:  "blue", | 				WordColor:  "blue", | ||||||
| 				Action:     "game over", | 				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 { | 		if fi.Room.RedCounter == 0 { | ||||||
| @@ -127,8 +140,11 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | |||||||
| 				WordColor:  "red", | 				WordColor:  "red", | ||||||
| 				Action:     "game over", | 				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 { | 	if err := saveFullInfo(fi); err != nil { | ||||||
| 		abortWithError(w, err.Error()) | 		abortWithError(w, err.Error()) | ||||||
| 		return | 		return | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"gralias/models" | 	"gralias/models" | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { | 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.IsRunning = true | ||||||
| 	fi.Room.IsOver = false | 	fi.Room.IsOver = false | ||||||
| 	fi.Room.TeamTurn = "blue" | 	fi.Room.TeamTurn = "blue" | ||||||
|  | 	fi.Room.OpenedThisTurn = 0 | ||||||
|  | 	fi.Room.ThisTurnLimit = 0 | ||||||
| 	loadCards(fi.Room) | 	loadCards(fi.Room) | ||||||
| 	fi.Room.UpdateCounter() | 	fi.Room.UpdateCounter() | ||||||
| 	fi.Room.TeamWon = "" | 	fi.Room.TeamWon = "" | ||||||
| @@ -226,6 +229,11 @@ func HandleGiveClue(w http.ResponseWriter, r *http.Request) { | |||||||
| 		abortWithError(w, err.Error()) | 		abortWithError(w, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	guessLimitU64, err := strconv.ParseUint(num, 10, 8) | ||||||
|  | 	if err != nil { | ||||||
|  | 		abortWithError(w, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	// validations === | 	// validations === | ||||||
| 	if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) { | 	if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) { | ||||||
| 		err = errors.New("not your team's move") | 		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.ActionHistory = append(fi.Room.ActionHistory, action) | ||||||
| 	fi.Room.MimeDone = true | 	fi.Room.MimeDone = true | ||||||
|  | 	fi.Room.ThisTurnLimit = uint8(guessLimitU64) + 1 | ||||||
| 	notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num) | 	notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num) | ||||||
| 	notifyBotIfNeeded(fi) | 	notifyBotIfNeeded(fi) | ||||||
| 	if err := saveFullInfo(fi); err != nil { | 	if err := saveFullInfo(fi); err != nil { | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package llmapi | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -29,7 +30,7 @@ func (p *deepSeekParser) ParseBytes(body []byte) (map[string]any, error) { | |||||||
| 	} | 	} | ||||||
| 	if len(dsResp.Choices) == 0 { | 	if len(dsResp.Choices) == 0 { | ||||||
| 		p.log.Error("empty choices", "dsResp", dsResp) | 		p.log.Error("empty choices", "dsResp", dsResp) | ||||||
| 		err := fmt.Errorf("empty choices in dsResp") | 		err := errors.New("empty choices in dsResp") | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	text := dsResp.Choices[0].Text | 	text := dsResp.Choices[0].Text | ||||||
| @@ -37,7 +38,7 @@ func (p *deepSeekParser) ParseBytes(body []byte) (map[string]any, error) { | |||||||
| 	ri := strings.LastIndex(text, "}") | 	ri := strings.LastIndex(text, "}") | ||||||
| 	if li < 0 || ri < 1 { | 	if li < 0 || ri < 1 { | ||||||
| 		p.log.Error("not a json", "msg", text) | 		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 | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	sj := text[li : ri+1] | 	sj := text[li : ri+1] | ||||||
| @@ -67,7 +68,7 @@ func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) { | |||||||
| 	} | 	} | ||||||
| 	if len(resp.Choices) == 0 { | 	if len(resp.Choices) == 0 { | ||||||
| 		p.log.Error("empty choices", "resp", resp) | 		p.log.Error("empty choices", "resp", resp) | ||||||
| 		err := fmt.Errorf("empty choices in resp") | 		err := errors.New("empty choices in resp") | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	text := resp.Choices[0].Message.Content | 	text := resp.Choices[0].Message.Content | ||||||
| @@ -75,7 +76,7 @@ func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) { | |||||||
| 	ri := strings.LastIndex(text, "}") | 	ri := strings.LastIndex(text, "}") | ||||||
| 	if li < 0 || ri < 1 { | 	if li < 0 || ri < 1 { | ||||||
| 		p.log.Error("not a json", "msg", text) | 		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 | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	sj := text[li : ri+1] | 	sj := text[li : ri+1] | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								main.go
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"gralias/config" | 	"gralias/config" | ||||||
| 	"gralias/handlers" | 	"gralias/handlers" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| @@ -19,7 +18,7 @@ func ListenToRequests(port string) error { | |||||||
| 	mux := http.NewServeMux() | 	mux := http.NewServeMux() | ||||||
| 	server := &http.Server{ | 	server := &http.Server{ | ||||||
| 		Handler:      handlers.LogRequests(handlers.GetSession(mux)), | 		Handler:      handlers.LogRequests(handlers.GetSession(mux)), | ||||||
| 		Addr:         fmt.Sprintf(":%s", port), | 		Addr:         ":" + port, | ||||||
| 		ReadTimeout:  time.Second * 5, // TODO: to cfg | 		ReadTimeout:  time.Second * 5, // TODO: to cfg | ||||||
| 		WriteTimeout: 0,               // sse streaming | 		WriteTimeout: 0,               // sse streaming | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -67,6 +67,8 @@ type Room struct { | |||||||
| 	RedTeam        Team | 	RedTeam        Team | ||||||
| 	BlueTeam       Team | 	BlueTeam       Team | ||||||
| 	Cards          []WordCard | 	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 | 	WCMap          map[string]WordColor | ||||||
| 	BotMap         map[string]BotPlayer // key is bot name | 	BotMap         map[string]BotPlayer // key is bot name | ||||||
| 	Result         uint8                // 0 for unknown; 1 is win for red; 2 if for blue; | 	Result         uint8                // 0 for unknown; 1 is win for red; 2 if for blue; | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								todos.md
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								todos.md
									
									
									
									
									
								
							| @@ -6,10 +6,9 @@ | |||||||
| - invite link; + | - invite link; + | ||||||
| - login with invite link; + | - login with invite link; + | ||||||
| - add html icons of whos turn it is (like an image of big ? when mime is thinking); | - 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; | - 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); | - 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; | - 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; | - hide clue input for mime when it's not their turn; | ||||||
| - needs resend to llm btn; + | - needs resend to llm btn; + | ||||||
|  |  | ||||||
| @@ -26,7 +25,8 @@ | |||||||
| ### issues | ### issues | ||||||
| - after the game started (isrunning) players should be able join guessers, but not switch team, or join as a mime; | - 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; | - 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); | - 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; | - after starting a new game (after old one) blue mime has no clue input; | ||||||
| - gameover to backlog; | - gameover to backlog; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder