Repos / gorts / 73c6a2b103
commit 73c6a2b103a750adaabea9a2a7868d1f7c3ed2d9
Author: Nhân <hi@imnhan.com>
Date:   Tue Jun 20 14:35:56 2023 +0700

    autosuggest names based on player list

diff --git a/main.go b/main.go
index e273d15..33bc0a2 100644
--- a/main.go
+++ b/main.go
@@ -85,6 +85,7 @@ func startGUI() {
 	fmt.Fprintln(stdin, mainTcl)
 	println("Loaded main tcl script.")
 
+	players := players.FromFile(PlayersFile)
 	state := initState()
 	b64icon := base64.StdEncoding.EncodeToString(gortsPngIcon)
 
@@ -139,13 +140,21 @@ func startGUI() {
 			state.Write()
 
 		case "readplayernames":
-			players := players.FromFile(PlayersFile)
 			for _, player := range players {
 				respond(player.Name)
 			}
 			respond("end")
 
+		case "searchplayers":
+			query := next()
+			for _, p := range players {
+				if p.MatchesName(query) {
+					respond(p.Name)
+				}
+			}
+			respond("end")
 		}
+
 	}
 
 	println("Tcl process terminated.")
diff --git a/players/players.go b/players/players.go
index efa601a..e68be90 100644
--- a/players/players.go
+++ b/players/players.go
@@ -4,6 +4,8 @@
 	"encoding/csv"
 	"log"
 	"os"
+	"regexp"
+	"strings"
 )
 
 type Player struct {
@@ -26,6 +28,8 @@ func FromFile(filepath string) []Player {
 	reader := csv.NewReader(f)
 	reader.FieldsPerRecord = 3
 	records, err := reader.ReadAll()
+	// TODO: should probably return error so GUI can show an error message
+	// instead of crashing.
 	if err != nil {
 		log.Fatalf("csv parse error for %s: %s", filepath, err)
 	}
@@ -41,3 +45,15 @@ func FromFile(filepath string) []Player {
 
 	return players
 }
+
+var nonAlphanumeric = regexp.MustCompile(`[^a-zA-Z0-9]+`)
+
+func normalize(in string) (out string) {
+	out = strings.ToLower(in)
+	out = nonAlphanumeric.ReplaceAllString(out, "")
+	return out
+}
+
+func (p *Player) MatchesName(query string) bool {
+	return normalize(query) == normalize(p.Name)
+}
diff --git a/tcl/main.tcl b/tcl/main.tcl
index 0539600..eca1954 100644
--- a/tcl/main.tcl
+++ b/tcl/main.tcl
@@ -145,6 +145,7 @@ proc initialize {b64icon webport countrycodes} {
     .c.players.p2country configure -values $countrycodes
 
     readplayernames
+    setup_player_name_suggestion
 }
 
 proc seticon {b64data} {
@@ -196,6 +197,31 @@ proc readplayernames {} {
     .c.players.p2name configure -values $playernames
 }
 
+proc setup_player_name_suggestion {} {
+    proc update_suggestions {_ key _} {
+        if {!($key == "p1name" || $key == "p2name")} {
+            return
+        }
+        set widget .c.players.$key
+        set newvalue $::scoreboard($key)
+        set matches [searchplayers $newvalue]
+        $widget configure -values $matches
+    }
+    trace add variable ::scoreboard write update_suggestions
+}
+
+proc searchplayers {query} {
+    set playernames {}
+    puts "searchplayers"
+    puts $query
+    set line [gets stdin]
+    while {$line != "end"} {
+        lappend playernames $line
+        set line [gets stdin]
+    }
+    return $playernames
+}
+
 proc discardstate {} {
     foreach key [array names ::scoreboard] {
         set ::scoreboard($key) $::applied_scoreboard($key)