Chore: refactor; mark words [WIP]
This commit is contained in:
		| @@ -10,10 +10,16 @@ | |||||||
|     </div> |     </div> | ||||||
|   {{end}} |   {{end}} | ||||||
| {{else}} | {{else}} | ||||||
| <div id="card-{{.Word}}" class="bg-stone-400 border border-gray-500 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  | <div id="card-{{.Word}}" class="bg-stone-400 border border-gray-500 rounded-lg min-w-[100px] cursor-pointer flex flex-col h-full"> | ||||||
|      style="text-shadow: 0 2px 4px rgba(0,0,0,0.8);" |      <div class="flex-grow text-center p-4 flex items-center justify-center text-white" | ||||||
|      hx-get="/word/show-color?word={{.Word}}" hx-trigger="click" hx-swap="outerHTML transition:true swap:.05s"> |           style="text-shadow: 0 2px 4px rgba(0,0,0,0.8);" | ||||||
|     {{.Word}} |           hx-get="/word/show-color?word={{.Word}}" hx-trigger="click" hx-swap="outerHTML transition:true swap:.05s"> | ||||||
|  |         {{.Word}} | ||||||
|  |      </div> | ||||||
|  |      <div class="h-6 bg-stone-600 rounded-b flex items-center justify-center text-white text-sm cursor-pointer" | ||||||
|  |           onclick="this.innerHTML = 'X';"> | ||||||
|  |           | ||||||
|  |      </div> | ||||||
| </div> | </div> | ||||||
| {{end}} | {{end}} | ||||||
| {{end}} | {{end}} | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import ( | |||||||
| 	"gralias/broker" | 	"gralias/broker" | ||||||
| 	"gralias/llmapi" | 	"gralias/llmapi" | ||||||
| 	"gralias/models" | 	"gralias/models" | ||||||
| 	"gralias/utils" |  | ||||||
| 	"gralias/wordloader" | 	"gralias/wordloader" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
| @@ -173,22 +172,22 @@ func getFullInfoByCtx(ctx context.Context) (*models.FullInfo, error) { | |||||||
| 	return resp, nil | 	return resp, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func leaveRole(fi *models.FullInfo) { | // // DEPRECATED | ||||||
| 	fi.Room.RedTeam.Guessers = utils.RemoveFromSlice(fi.State.Username, fi.Room.RedTeam.Guessers) | // func leaveRole(fi *models.FullInfo) { | ||||||
| 	fi.Room.BlueTeam.Guessers = utils.RemoveFromSlice(fi.State.Username, fi.Room.BlueTeam.Guessers) | // 	fi.Room.RedTeam.Guessers = utils.RemoveFromSlice(fi.State.Username, fi.Room.RedTeam.Guessers) | ||||||
| 	if fi.Room.RedTeam.Mime == fi.State.Username { | // 	fi.Room.BlueTeam.Guessers = utils.RemoveFromSlice(fi.State.Username, fi.Room.BlueTeam.Guessers) | ||||||
| 		fi.Room.RedTeam.Mime = "" | // 	if fi.Room.RedTeam.Mime == fi.State.Username { | ||||||
| 	} | // 		fi.Room.RedTeam.Mime = "" | ||||||
| 	if fi.Room.BlueTeam.Mime == fi.State.Username { | // 	} | ||||||
| 		fi.Room.BlueTeam.Mime = "" | // 	if fi.Room.BlueTeam.Mime == fi.State.Username { | ||||||
| 	} | // 		fi.Room.BlueTeam.Mime = "" | ||||||
| } | // 	} | ||||||
|  | // } | ||||||
|  |  | ||||||
| func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error) { | func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error) { | ||||||
| 	// get username | 	// get username | ||||||
| 	fi, _ := getFullInfoByCtx(ctx) | 	fi, _ := getFullInfoByCtx(ctx) | ||||||
| 	// leave gueesers if present | 	fi.Room.RemovePlayer(fi.State.Username) | ||||||
| 	leaveRole(fi) |  | ||||||
| 	// get room | 	// get room | ||||||
| 	if role == "mime" { | 	if role == "mime" { | ||||||
| 		if team == "blue" { | 		if team == "blue" { | ||||||
| @@ -367,3 +366,31 @@ func recoverPlayer(pm map[string]string) error { | |||||||
| 	} | 	} | ||||||
| 	return saveState(pm["Username"], us) | 	return saveState(pm["Username"], us) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // validateMove checks if it is players turn | ||||||
|  | func validateMove(fi *models.FullInfo, ur models.UserRole) error { | ||||||
|  | 	if fi.State.Role != ur { | ||||||
|  | 		err := fmt.Errorf("need to be %s to make that action", ur) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// whos move it is? | ||||||
|  | 	if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) { | ||||||
|  | 		err := errors.New("not your team's move") | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	switch ur { | ||||||
|  | 	case models.UserRoleGuesser: | ||||||
|  | 		if !fi.Room.MimeDone { | ||||||
|  | 			err := errors.New("wait for the mime to give a clue") | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	case models.UserRoleMime: | ||||||
|  | 		if fi.Room.MimeDone { | ||||||
|  | 			err := errors.New("clue was already given") | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("uknown user role: %s", ur) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| package handlers | package handlers | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"gralias/llmapi" | 	"gralias/llmapi" | ||||||
| 	"gralias/models" | 	"gralias/models" | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func HandleShowCreateForm(w http.ResponseWriter, r *http.Request) { | func HandleShowCreateForm(w http.ResponseWriter, r *http.Request) { | ||||||
| @@ -46,21 +46,10 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | |||||||
| 		abortWithError(w, err.Error()) | 		abortWithError(w, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if fi.State.Role != "guesser" { | 	if err := validateMove(fi, models.UserRoleGuesser); err != nil { | ||||||
| 		err = errors.New("need to be guesser to open the card") |  | ||||||
| 		abortWithError(w, err.Error()) | 		abortWithError(w, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	// whos move it is? |  | ||||||
| 	if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) { |  | ||||||
| 		err = errors.New("not your team's move") |  | ||||||
| 		abortWithError(w, err.Error()) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if !fi.Room.MimeDone { |  | ||||||
| 		abortWithError(w, "wait for the clue") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	color, exists := fi.Room.WCMap[word] | 	color, exists := fi.Room.WCMap[word] | ||||||
| 	log.Debug("got show-color request", "word", word, "color", color) | 	log.Debug("got show-color request", "word", word, "color", color) | ||||||
| 	if !exists { | 	if !exists { | ||||||
| @@ -159,6 +148,135 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func HandleMarkCard(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	word := r.URL.Query().Get("word") | ||||||
|  | 	ctx := r.Context() | ||||||
|  | 	tmpl, err := template.ParseGlob("components/*.html") | ||||||
|  | 	if err != nil { | ||||||
|  | 		abortWithError(w, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	fi, err := getFullInfoByCtx(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		abortWithError(w, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err := validateMove(fi, models.UserRoleGuesser); err != nil { | ||||||
|  | 		abortWithError(w, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	color, exists := fi.Room.WCMap[word] | ||||||
|  | 	log.Debug("got show-color request", "word", word, "color", color) | ||||||
|  | 	if !exists { | ||||||
|  | 		abortWithError(w, "word is not found") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// check if card already was revealed | ||||||
|  | 	for i, card := range fi.Room.Cards { | ||||||
|  | 		if !strings.EqualFold(card.Word, word) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if card.Revealed { | ||||||
|  | 			abortWithError(w, "cannot mark already revealed") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		fi.Room.Cards[i].Mark = append(card.Mark, models.CardMark{ | ||||||
|  | 			Username: fi.State.Username, | ||||||
|  | 			Active:   true, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 	cardword := models.WordCard{ | ||||||
|  | 		Word:     word, | ||||||
|  | 		Color:    color, | ||||||
|  | 		Revealed: false, | ||||||
|  | 	} | ||||||
|  | 	fi.Room.RevealSpecificWord(word) | ||||||
|  | 	fi.Room.UpdateCounter() | ||||||
|  | 	action := models.Action{ | ||||||
|  | 		Actor:      fi.State.Username, | ||||||
|  | 		ActorColor: string(fi.State.Team), | ||||||
|  | 		WordColor:  string(color), | ||||||
|  | 		Action:     models.ActionTypeGuess, | ||||||
|  | 		Word:       word, | ||||||
|  | 	} | ||||||
|  | 	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 > 0 { | ||||||
|  | 		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 | ||||||
|  | 		fi.Room.IsRunning = false | ||||||
|  | 		fi.Room.IsOver = true | ||||||
|  | 		fi.Room.TeamWon = oppositeColor | ||||||
|  | 		action := models.Action{ | ||||||
|  | 			Actor:      fi.State.Username, | ||||||
|  | 			ActorColor: string(fi.State.Team), | ||||||
|  | 			WordColor:  models.WordColorBlack, | ||||||
|  | 			Action:     models.ActionTypeGameOver, | ||||||
|  | 		} | ||||||
|  | 		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 | ||||||
|  | 		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:  models.WordColorBlue, | ||||||
|  | 				Action:     models.ActionTypeGameOver, | ||||||
|  | 			} | ||||||
|  | 			fi.Room.OpenedThisTurn = 0 | ||||||
|  | 			fi.Room.ThisTurnLimit = 0 | ||||||
|  | 			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:  models.WordColorRed, | ||||||
|  | 				Action:     models.ActionTypeGameOver, | ||||||
|  | 			} | ||||||
|  | 			fi.Room.OpenedThisTurn = 0 | ||||||
|  | 			fi.Room.ThisTurnLimit = 0 | ||||||
|  | 			fi.Room.ActionHistory = append(fi.Room.ActionHistory, action) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if err := saveFullInfo(fi); err != nil { | ||||||
|  | 		abortWithError(w, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// get mime bot for opp team and notify it | ||||||
|  | 	notifyBotIfNeeded(fi) | ||||||
|  | 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") | ||||||
|  | 	if err := tmpl.ExecuteTemplate(w, "cardword", cardword); err != nil { | ||||||
|  | 		log.Error("failed to execute cardword template", "error", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func HandleActionHistory(w http.ResponseWriter, r *http.Request) { | func HandleActionHistory(w http.ResponseWriter, r *http.Request) { | ||||||
| 	fi, err := getFullInfoByCtx(r.Context()) | 	fi, err := getFullInfoByCtx(r.Context()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -232,6 +232,8 @@ func RemoveBot(botName string, room *models.Room) error { | |||||||
| 	delete(room.BotMap, botName) | 	delete(room.BotMap, botName) | ||||||
| 	delete(DoneChanMap, botName) | 	delete(DoneChanMap, botName) | ||||||
| 	delete(SignalChanMap, botName) | 	delete(SignalChanMap, botName) | ||||||
|  | 	// remove role from room | ||||||
|  | 	room.RemovePlayer(botName) | ||||||
| 	return saveRoom(room) | 	return saveRoom(room) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								main.go
									
									
									
									
									
								
							| @@ -48,6 +48,7 @@ func ListenToRequests(port string) *http.Server { | |||||||
| 	mux.HandleFunc("POST /check/name", handlers.HandleNameCheck) | 	mux.HandleFunc("POST /check/name", handlers.HandleNameCheck) | ||||||
| 	mux.HandleFunc("GET /add-bot", handlers.HandleAddBot) | 	mux.HandleFunc("GET /add-bot", handlers.HandleAddBot) | ||||||
| 	mux.HandleFunc("GET /remove-bot", handlers.HandleRemoveBot) | 	mux.HandleFunc("GET /remove-bot", handlers.HandleRemoveBot) | ||||||
|  | 	mux.HandleFunc("GET /mark-card", handlers.HandleMarkCard) | ||||||
| 	// special | 	// special | ||||||
| 	mux.HandleFunc("GET /renotify-bot", handlers.HandleRenotifyBot) | 	mux.HandleFunc("GET /renotify-bot", handlers.HandleRenotifyBot) | ||||||
| 	// sse | 	// sse | ||||||
|   | |||||||
| @@ -63,6 +63,11 @@ type BotPlayer struct { | |||||||
| 	Team UserTeam | 	Team UserTeam | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type CardMark struct { | ||||||
|  | 	Username string | ||||||
|  | 	Active   bool | ||||||
|  | } | ||||||
|  |  | ||||||
| type Room struct { | type Room struct { | ||||||
| 	ID             string    `json:"id" db:"id"` | 	ID             string    `json:"id" db:"id"` | ||||||
| 	CreatedAt      time.Time `json:"created_at" db:"created_at"` // limit? | 	CreatedAt      time.Time `json:"created_at" db:"created_at"` // limit? | ||||||
| @@ -89,11 +94,24 @@ type Room struct { | |||||||
| 	RoundTime      int32  `json:"round_time"` | 	RoundTime      int32  `json:"round_time"` | ||||||
| 	IsOver         bool | 	IsOver         bool | ||||||
| 	TeamWon        UserTeam // blue | red | 	TeamWon        UserTeam // blue | red | ||||||
|  | 	// | ||||||
|  | 	Mark CardMark // card is marked | ||||||
| 	// needed for debug | 	// needed for debug | ||||||
| 	LogJournal   []string | 	LogJournal   []string | ||||||
| 	LastActionTS time.Time | 	LastActionTS time.Time | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (r *Room) RemovePlayer(username string) { | ||||||
|  | 	r.RedTeam.Guessers = utils.RemoveFromSlice(username, r.RedTeam.Guessers) | ||||||
|  | 	r.BlueTeam.Guessers = utils.RemoveFromSlice(username, r.BlueTeam.Guessers) | ||||||
|  | 	if r.RedTeam.Mime == username { | ||||||
|  | 		r.RedTeam.Mime = "" | ||||||
|  | 	} | ||||||
|  | 	if r.BlueTeam.Mime == username { | ||||||
|  | 		r.BlueTeam.Mime = "" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // FindBotByTeamRole returns bot name if found; otherwise empty string | // FindBotByTeamRole returns bot name if found; otherwise empty string | ||||||
| func (r *Room) FindBotByTeamRole(team, role string) string { | func (r *Room) FindBotByTeamRole(team, role string) string { | ||||||
| 	for bn, b := range r.BotMap { | 	for bn, b := range r.BotMap { | ||||||
| @@ -249,9 +267,10 @@ func (r *Room) RevealSpecificWord(word string) { | |||||||
| } | } | ||||||
|  |  | ||||||
| type WordCard struct { | type WordCard struct { | ||||||
| 	Word     string    `json:"word"` | 	Word     string     `json:"word"` | ||||||
| 	Color    WordColor `json:"color"` | 	Color    WordColor  `json:"color"` | ||||||
| 	Revealed bool      `json:"revealed"` | 	Revealed bool       `json:"revealed"` | ||||||
|  | 	Mark     []CardMark `json:"marks"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type GameSettings struct { | type GameSettings struct { | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								todos.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								todos.md
									
									
									
									
									
								
							| @@ -45,3 +45,5 @@ | |||||||
| - guesser bot no request after game restart; | - guesser bot no request after game restart; | ||||||
| - if mime joins another role, he stays as mime (before game start); | - if mime joins another role, he stays as mime (before game start); | ||||||
| - guesser llm makes up words, likely the prompt should be more clear; | - guesser llm makes up words, likely the prompt should be more clear; | ||||||
|  | - remove bot does not remove for player roles in the room; + | ||||||
|  | - remove join as mime button if there is a mime already on that team; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder