From 4b6c0a9bf49b2cebe4de773414d3d6722d45c269 Mon Sep 17 00:00:00 2001 From: Mariano Gappa Date: Thu, 27 Jun 2024 21:54:59 +0100 Subject: [PATCH] Add random bot. Fix bug where cards run out. --- examplebot/main.go | 51 +++++++++++++++++++++++++++++++++++++++++++++ exampleclient/ui.go | 4 ++++ main.go | 49 ++++++++++++++++++++++++++++++++----------- truco/deck.go | 4 ++++ truco/truco.go | 1 + 5 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 examplebot/main.go diff --git a/examplebot/main.go b/examplebot/main.go new file mode 100644 index 0000000..7b3b841 --- /dev/null +++ b/examplebot/main.go @@ -0,0 +1,51 @@ +package examplebot + +import ( + "fmt" + "log" + "math/rand" + + "github.com/gorilla/websocket" + "github.com/marianogappa/truco/server" + "github.com/marianogappa/truco/truco" +) + +func Bot(playerID int, address string) { + // Open the WebSocket connection, and send a hello message. + + conn, _, err := websocket.DefaultDialer.Dial(fmt.Sprintf("ws://%v/ws", address), nil) + if err != nil { + log.Fatalf("Failed to connect to WebSocket server: %v", err) + } + defer conn.Close() + + // Hello message is meant to tell the server who we are, and request game state. + // Game could be in progress (this could be a reconnection). + if err := server.WsSend(conn, server.NewMessageHello(playerID)); err != nil { + log.Fatal(err) + } + + // On each iteration + for { + clientGameState, err := server.WsReadMessage[truco.ClientGameState, server.MessageHeresGameState](conn, server.MessageTypeHeresGameState) + if err != nil { + log.Fatal(err) + } + + if clientGameState.IsGameEnded { + return + } + + if clientGameState.TurnPlayerID != playerID { + continue + } + + // Get a random element from clientGameState.PossibleActions. + randomAction := clientGameState.PossibleActions[rand.Intn(len(clientGameState.PossibleActions))] + + // Send the action to the server. + if err := server.WsSend(conn, server.MessageAction{WebsocketMessage: server.WebsocketMessage{Type: server.MessageTypeAction}, Action: randomAction}); err != nil { + log.Fatal(err) + } + } +} diff --git a/exampleclient/ui.go b/exampleclient/ui.go index 943e8f2..e5478ef 100644 --- a/exampleclient/ui.go +++ b/exampleclient/ui.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "strings" + "time" "github.com/marianogappa/truco/truco" "github.com/nsf/termbox-go" @@ -88,6 +89,9 @@ func (u *ui) render(state truco.ClientGameState, mode renderMode) error { renderActions(rs) termbox.Flush() + // This is an artificial delay to make the game more human-like. + time.Sleep(1 * time.Second) + return nil } diff --git a/main.go b/main.go index a5695f4..cba4126 100644 --- a/main.go +++ b/main.go @@ -3,37 +3,62 @@ package main import ( "fmt" "os" + "strconv" + "github.com/marianogappa/truco/examplebot" "github.com/marianogappa/truco/exampleclient" "github.com/marianogappa/truco/server" ) func main() { if len(os.Args) < 2 { - fmt.Println("usage: truco server") - fmt.Println("usage: truco player1|player2 [address]") - fmt.Println("Define the PORT environment variable for truco server to change the default port (8080).") - os.Exit(0) + usage() } port := os.Getenv("PORT") if port == "" { port = "8080" } + cmd := os.Args[1] + address := fmt.Sprintf("localhost:%v", port) - if len(os.Args) >= 3 { - address = os.Args[2] + if len(os.Args) >= 4 { + address = os.Args[3] + } + + var ( + playerNum int + err error + ) + if cmd == "player" || cmd == "bot" { + playerNum, err = strconv.Atoi(os.Args[2]) + if err != nil { + fmt.Println("Invalid player number. Please provide a number.") + usage() + } } - arg := os.Args[1] - switch arg { + switch cmd { case "server": server.New(port).Start() - case "player1": - exampleclient.Player(0, address) - case "player2": - exampleclient.Player(1, address) + case "player": + exampleclient.Player(playerNum-1, address) + case "bot": + examplebot.Bot(playerNum-1, address) default: fmt.Println("Invalid argument. Please provide either server or client.") } } + +func usage() { + fmt.Println("usage: truco server") + fmt.Println("usage: truco player %number [address]") + fmt.Println("usage: truco bot %number [address]") + fmt.Println("usage: e.g. truco player 1") + fmt.Println("usage: e.g. truco player 2") + fmt.Println("usage: e.g. truco player 1 localhost:8080") + fmt.Println("usage: e.g. truco bot 1 localhost:8080") + fmt.Println("usage: e.g. truco bot 2") + fmt.Println("Define the PORT environment variable for truco server to change the default port (8080).") + os.Exit(1) +} diff --git a/truco/deck.go b/truco/deck.go index f39e9a4..e8051a4 100644 --- a/truco/deck.go +++ b/truco/deck.go @@ -110,6 +110,10 @@ func newDeck() *deck { return &d } +func (d *deck) shuffle() { + d.cards = makeSpanishCards() +} + func (d *deck) dealHand() *Hand { return d.dealHandFunc() } diff --git a/truco/truco.go b/truco/truco.go index 6f7cbc1..e3dbdbc 100644 --- a/truco/truco.go +++ b/truco/truco.go @@ -151,6 +151,7 @@ func New(opts ...func(*GameState)) *GameState { } func (g *GameState) startNewRound() { + g.deck.shuffle() g.RoundTurnPlayerID = g.OpponentOf(g.RoundTurnPlayerID) g.RoundNumber++ g.TurnPlayerID = g.RoundTurnPlayerID