From 95d7564e3c89275ba459486f181a18ed75678c4e Mon Sep 17 00:00:00 2001 From: wagslane Date: Tue, 2 Apr 2024 12:38:06 -0600 Subject: [PATCH 1/7] first --- .gitignore | 1 + README.md | 29 ++++++++- cmd/client/main.go | 7 +++ cmd/server/main.go | 7 +++ go.mod | 3 + go.sum | 0 internal/gamelogic/gamedata.go | 52 ++++++++++++++++ internal/gamelogic/gamelogic.go | 92 ++++++++++++++++++++++++++++ internal/gamelogic/gamestate.go | 96 +++++++++++++++++++++++++++++ internal/gamelogic/logs.go | 29 +++++++++ internal/gamelogic/move.go | 90 +++++++++++++++++++++++++++ internal/gamelogic/pause.go | 19 ++++++ internal/gamelogic/spawn.go | 34 +++++++++++ internal/gamelogic/war.go | 105 ++++++++++++++++++++++++++++++++ internal/routing/models.go | 13 ++++ internal/routing/routing.go | 16 +++++ rabbit.sh | 1 + 17 files changed, 593 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 cmd/client/main.go create mode 100644 cmd/server/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/gamelogic/gamedata.go create mode 100644 internal/gamelogic/gamelogic.go create mode 100644 internal/gamelogic/gamestate.go create mode 100644 internal/gamelogic/logs.go create mode 100644 internal/gamelogic/move.go create mode 100644 internal/gamelogic/pause.go create mode 100644 internal/gamelogic/spawn.go create mode 100644 internal/gamelogic/war.go create mode 100644 internal/routing/models.go create mode 100644 internal/routing/routing.go create mode 100755 rabbit.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/README.md b/README.md index b011aaa..92e5ceb 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ -# learn-pub-sub-starter \ No newline at end of file +# learn-pub-sub-starter (Peril) + +This is the starter code used in Boot.dev's [Learn Pub/Sub](https://learn.boot.dev/learn-pub-sub) course. It contains: + +* The `internal/gamelogic` package, which contains the game logic for the Peril game. +* The `internal/routing` package, which contains some routing constants for the game. +* Stubs of the `cmd/client` and `cmd/server` packages, which are `main` packages that run the client and server for the game. +* The `rabbitmq.sh` script, which starts a RabbitMQ server in a Docker container. + +## Running the Game + +Make sure you have a RabbitMQ server running locally: + +```bash +./rabbitmq.sh +``` + +Using separate terminal windows, you can run clients and servers: + +```bash +go run ./cmd/server +``` + +```bash +go run ./cmd/client +``` + +You will be working in this repository and editing it's files throughout the course. You can peek the solution at any time in the course to make sure you're on the right track. diff --git a/cmd/client/main.go b/cmd/client/main.go new file mode 100644 index 0000000..74de144 --- /dev/null +++ b/cmd/client/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Starting Peril client...") +} diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..58622c6 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Starting Peril server...") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6bfacb3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/bootdotdev/learn-pub-sub-starter + +go 1.22.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/internal/gamelogic/gamedata.go b/internal/gamelogic/gamedata.go new file mode 100644 index 0000000..e361108 --- /dev/null +++ b/internal/gamelogic/gamedata.go @@ -0,0 +1,52 @@ +package gamelogic + +type Player struct { + Username string + Units map[int]Unit +} + +type UnitRank string + +const ( + RankInfantry = "infantry" + RankCavalry = "cavalry" + RankArtillery = "artillery" +) + +type Unit struct { + ID int + Rank UnitRank + Location Location +} + +type ArmyMove struct { + Player Player + Units []Unit + ToLocation Location +} + +type RecognitionOfWar struct { + Attacker Player + Defender Player +} + +type Location string + +func getAllRanks() map[UnitRank]struct{} { + return map[UnitRank]struct{}{ + RankInfantry: {}, + RankCavalry: {}, + RankArtillery: {}, + } +} + +func getAllLocations() map[Location]struct{} { + return map[Location]struct{}{ + "americas": {}, + "europe": {}, + "africa": {}, + "asia": {}, + "australia": {}, + "antarctica": {}, + } +} diff --git a/internal/gamelogic/gamelogic.go b/internal/gamelogic/gamelogic.go new file mode 100644 index 0000000..ec617f8 --- /dev/null +++ b/internal/gamelogic/gamelogic.go @@ -0,0 +1,92 @@ +package gamelogic + +import ( + "bufio" + "errors" + "fmt" + "math/rand" + "os" + "strings" +) + +func PrintClientHelp() { + fmt.Println("Possible commands:") + fmt.Println("* move ...") + fmt.Println(" example:") + fmt.Println(" move asia 1") + fmt.Println("* spawn ") + fmt.Println(" example:") + fmt.Println(" spawn europe infantry") + fmt.Println("* status") + fmt.Println("* spam ") + fmt.Println(" example:") + fmt.Println(" spam 5") + fmt.Println("* quit") + fmt.Println("* help") +} + +func ClientWelcome() (string, error) { + fmt.Println("Welcome to the Peril client!") + fmt.Println("Please enter your username:") + words := GetInput() + if len(words) == 0 { + return "", errors.New("you must enter a username. goodbye") + } + username := words[0] + fmt.Printf("Welcome, %s!\n", username) + PrintClientHelp() + return username, nil +} + +func PrintServerHelp() { + fmt.Println("Possible commands:") + fmt.Println("* pause") + fmt.Println("* resume") + fmt.Println("* quit") + fmt.Println("* help") +} + +func GetInput() []string { + fmt.Print("> ") + scanner := bufio.NewScanner(os.Stdin) + scanned := scanner.Scan() + if !scanned { + return nil + } + line := scanner.Text() + line = strings.TrimSpace(line) + return strings.Fields(line) +} + +func GetMaliciousLog() string { + possibleLogs := []string{ + "Never interrupt your enemy when he is making a mistake.", + "The hardest thing of all for a soldier is to retreat.", + "A soldier will fight long and hard for a bit of colored ribbon.", + "It is well that war is so terrible, otherwise we should grow too fond of it.", + "The art of war is simple enough. Find out where your enemy is. Get at him as soon as you can. Strike him as hard as you can, and keep moving on.", + "All warfare is based on deception.", + } + randomIndex := rand.Intn(len(possibleLogs)) + msg := possibleLogs[randomIndex] + return msg +} + +func PrintQuit() { + fmt.Println("I hate this game! (╯°□°)╯︵ ┻━┻") +} + +func (gs *GameState) CommandStatus() { + if gs.isPaused() { + fmt.Println("The game is paused.") + return + } else { + fmt.Println("The game is not paused.") + } + + p := gs.GetPlayerSnap() + fmt.Printf("You are %s, and you have %d units.\n", p.Username, len(p.Units)) + for _, unit := range p.Units { + fmt.Printf("* %v: %v, %v\n", unit.ID, unit.Location, unit.Rank) + } +} diff --git a/internal/gamelogic/gamestate.go b/internal/gamelogic/gamestate.go new file mode 100644 index 0000000..698f37a --- /dev/null +++ b/internal/gamelogic/gamestate.go @@ -0,0 +1,96 @@ +package gamelogic + +import ( + "sync" +) + +type GameState struct { + Player Player + Paused bool + mu *sync.RWMutex +} + +func NewGameState(username string) *GameState { + return &GameState{ + Player: Player{ + Username: username, + Units: map[int]Unit{}, + }, + Paused: false, + mu: &sync.RWMutex{}, + } +} + +func (gs *GameState) resumeGame() { + gs.mu.Lock() + defer gs.mu.Unlock() + gs.Paused = false +} + +func (gs *GameState) pauseGame() { + gs.mu.Lock() + defer gs.mu.Unlock() + gs.Paused = true +} + +func (gs *GameState) isPaused() bool { + gs.mu.RLock() + defer gs.mu.RUnlock() + return gs.Paused +} + +func (gs *GameState) addUnit(u Unit) { + gs.mu.Lock() + defer gs.mu.Unlock() + gs.Player.Units[u.ID] = u +} + +func (gs *GameState) removeUnitsInLocation(loc Location) { + gs.mu.Lock() + defer gs.mu.Unlock() + for k, v := range gs.Player.Units { + if v.Location == loc { + delete(gs.Player.Units, k) + } + } +} + +func (gs *GameState) UpdateUnit(u Unit) { + gs.mu.Lock() + defer gs.mu.Unlock() + gs.Player.Units[u.ID] = u +} + +func (gs *GameState) GetUsername() string { + return gs.Player.Username +} + +func (gs *GameState) getUnitsSnap() []Unit { + gs.mu.RLock() + defer gs.mu.RUnlock() + Units := []Unit{} + for _, v := range gs.Player.Units { + Units = append(Units, v) + } + return Units +} + +func (gs *GameState) GetUnit(id int) (Unit, bool) { + gs.mu.RLock() + defer gs.mu.RUnlock() + u, ok := gs.Player.Units[id] + return u, ok +} + +func (gs *GameState) GetPlayerSnap() Player { + gs.mu.RLock() + defer gs.mu.RUnlock() + Units := map[int]Unit{} + for k, v := range gs.Player.Units { + Units[k] = v + } + return Player{ + Username: gs.Player.Username, + Units: Units, + } +} diff --git a/internal/gamelogic/logs.go b/internal/gamelogic/logs.go new file mode 100644 index 0000000..df40dc0 --- /dev/null +++ b/internal/gamelogic/logs.go @@ -0,0 +1,29 @@ +package gamelogic + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/bootdotdev/learn-pub-sub-starter/internal/routing" +) + +const logsFile = "game.log" + +func WriteLog(gamelog routing.GameLog) error { + log.Printf("received game log...") + + f, err := os.OpenFile(logsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("could not open logs file: %v", err) + } + defer f.Close() + + str := fmt.Sprintf("%v %v: %v\n", gamelog.CurrentTime.Format(time.RFC3339), gamelog.Username, gamelog.Message) + _, err = f.WriteString(str) + if err != nil { + return fmt.Errorf("could not write to logs file: %v", err) + } + return nil +} diff --git a/internal/gamelogic/move.go b/internal/gamelogic/move.go new file mode 100644 index 0000000..6607d5d --- /dev/null +++ b/internal/gamelogic/move.go @@ -0,0 +1,90 @@ +package gamelogic + +import ( + "errors" + "fmt" + "strconv" +) + +type MoveOutcome int + +const ( + MoveOutcomeSamePlayer MoveOutcome = iota + MoveOutComeSafe + MoveOutcomeMakeWar +) + +func (gs *GameState) HandleMove(move ArmyMove) MoveOutcome { + defer fmt.Println("------------------------") + player := gs.GetPlayerSnap() + + fmt.Println() + fmt.Println("==== Move Detected ====") + fmt.Printf("%s is moving %v unit(s) to %s\n", move.Player.Username, len(move.Units), move.ToLocation) + for _, unit := range move.Units { + fmt.Printf("* %v\n", unit.Rank) + } + + if player.Username == move.Player.Username { + return MoveOutcomeSamePlayer + } + + overlappingLocation := getOverlappingLocation(player, move.Player) + if overlappingLocation != "" { + fmt.Printf("You have units in %s! You are at war with %s!\n", overlappingLocation, move.Player.Username) + return MoveOutcomeMakeWar + } + fmt.Printf("You are safe from %s's units.\n", move.Player.Username) + return MoveOutComeSafe +} + +func getOverlappingLocation(p1 Player, p2 Player) Location { + for _, u1 := range p1.Units { + for _, u2 := range p2.Units { + if u1.Location == u2.Location { + return u1.Location + } + } + } + return "" +} + +func (gs *GameState) CommandMove(words []string) (ArmyMove, error) { + if gs.isPaused() { + return ArmyMove{}, errors.New("the game is paused, you can not move units") + } + if len(words) < 3 { + return ArmyMove{}, errors.New("usage: move etc") + } + newLocation := Location(words[1]) + locations := getAllLocations() + if _, ok := locations[newLocation]; !ok { + return ArmyMove{}, fmt.Errorf("error: %s is not a valid location", newLocation) + } + unitIDs := []int{} + for _, word := range words[2:] { + id := word + unitID, err := strconv.Atoi(id) + if err != nil { + return ArmyMove{}, fmt.Errorf("error: %s is not a valid unit ID", id) + } + unitIDs = append(unitIDs, unitID) + } + + for _, unitID := range unitIDs { + unit, ok := gs.GetUnit(unitID) + if !ok { + return ArmyMove{}, fmt.Errorf("error: unit with ID %v not found", unitID) + } + unit.Location = newLocation + gs.UpdateUnit(unit) + } + + mv := ArmyMove{ + ToLocation: newLocation, + Units: gs.getUnitsSnap(), + Player: gs.GetPlayerSnap(), + } + + return mv, nil +} diff --git a/internal/gamelogic/pause.go b/internal/gamelogic/pause.go new file mode 100644 index 0000000..5874f3c --- /dev/null +++ b/internal/gamelogic/pause.go @@ -0,0 +1,19 @@ +package gamelogic + +import ( + "fmt" + + "github.com/bootdotdev/learn-pub-sub-starter/internal/routing" +) + +func (gs *GameState) HandlePause(ps routing.PlayingState) { + defer fmt.Println("------------------------") + fmt.Println() + if ps.IsPaused { + fmt.Println("==== Pause Detected ====") + gs.pauseGame() + } else { + fmt.Println("==== Resume Detected ====") + gs.resumeGame() + } +} diff --git a/internal/gamelogic/spawn.go b/internal/gamelogic/spawn.go new file mode 100644 index 0000000..dd68d41 --- /dev/null +++ b/internal/gamelogic/spawn.go @@ -0,0 +1,34 @@ +package gamelogic + +import ( + "errors" + "fmt" +) + +func (gs *GameState) CommandSpawn(words []string) error { + if len(words) < 3 { + return errors.New("usage: spawn ") + } + + locationName := words[1] + locations := getAllLocations() + if _, ok := locations[Location(locationName)]; !ok { + return fmt.Errorf("error: %s is not a valid location", locationName) + } + + rank := words[2] + units := getAllRanks() + if _, ok := units[UnitRank(rank)]; !ok { + return fmt.Errorf("error: %s is not a valid unit", rank) + } + + id := len(gs.getUnitsSnap()) + 1 + gs.addUnit(Unit{ + ID: id, + Rank: UnitRank(rank), + Location: Location(locationName), + }) + + fmt.Printf("Spawned a(n) %s in %s with id %v\n", rank, locationName, id) + return nil +} diff --git a/internal/gamelogic/war.go b/internal/gamelogic/war.go new file mode 100644 index 0000000..39e9419 --- /dev/null +++ b/internal/gamelogic/war.go @@ -0,0 +1,105 @@ +package gamelogic + +import ( + "fmt" +) + +type WarOutcome int + +const ( + WarOutcomeNotInvolved WarOutcome = iota + WarOutcomeNoUnits + WarOutcomeYouWon + WarOutcomeOpponentWon + WarOutcomeDraw +) + +func (gs *GameState) HandleWar(rw RecognitionOfWar) (outcome WarOutcome, winner string, loser string) { + defer fmt.Println("------------------------") + fmt.Println() + fmt.Println("==== War Declared ====") + fmt.Printf("%s has declared war on %s!\n", rw.Attacker.Username, rw.Defender.Username) + + player := gs.GetPlayerSnap() + + if player.Username == rw.Defender.Username { + fmt.Printf("%s, you published the war.\n", player.Username) + return WarOutcomeNotInvolved, "", "" + } + + if player.Username != rw.Attacker.Username { + fmt.Printf("%s, you are not involved in this war.\n", player.Username) + return WarOutcomeNotInvolved, "", "" + } + + overlappingLocation := getOverlappingLocation(rw.Attacker, rw.Defender) + if overlappingLocation == "" { + fmt.Printf("Error! No units are in the same location. No war will be fought.\n") + return WarOutcomeNoUnits, "", "" + } + + attackerUnits := []Unit{} + defenderUnits := []Unit{} + for _, unit := range rw.Attacker.Units { + if unit.Location == overlappingLocation { + attackerUnits = append(attackerUnits, unit) + } + } + for _, unit := range rw.Defender.Units { + if unit.Location == overlappingLocation { + defenderUnits = append(defenderUnits, unit) + } + } + + fmt.Printf("%s's units:\n", rw.Attacker.Username) + for _, unit := range attackerUnits { + fmt.Printf(" * %v\n", unit.Rank) + } + fmt.Printf("%s's units:\n", rw.Defender.Username) + for _, unit := range defenderUnits { + fmt.Printf(" * %v\n", unit.Rank) + } + attackerPower := unitsToPowerLevel(attackerUnits) + defenderPower := unitsToPowerLevel(defenderUnits) + fmt.Printf("Attacker has a power level of %v\n", attackerPower) + fmt.Printf("Defender has a power level of %v\n", defenderPower) + if attackerPower > defenderPower { + fmt.Printf("%s has won the war!\n", rw.Attacker.Username) + if player.Username == rw.Defender.Username { + fmt.Println("You have lost the war!") + gs.removeUnitsInLocation(overlappingLocation) + fmt.Printf("Your units in %s have been killed.\n", overlappingLocation) + return WarOutcomeOpponentWon, rw.Attacker.Username, rw.Defender.Username + } + return WarOutcomeYouWon, rw.Attacker.Username, rw.Defender.Username + } else if defenderPower > attackerPower { + fmt.Printf("%s has won the war!\n", rw.Defender.Username) + if player.Username == rw.Attacker.Username { + fmt.Println("You have lost the war!") + gs.removeUnitsInLocation(overlappingLocation) + fmt.Printf("Your units in %s have been killed.\n", overlappingLocation) + return WarOutcomeOpponentWon, rw.Defender.Username, rw.Attacker.Username + } + return WarOutcomeYouWon, rw.Defender.Username, rw.Attacker.Username + } + fmt.Println("The war ended in a draw!") + fmt.Printf("Your units in %s have been killed.\n", overlappingLocation) + gs.removeUnitsInLocation(overlappingLocation) + return WarOutcomeDraw, rw.Attacker.Username, rw.Defender.Username +} + +func unitsToPowerLevel(units []Unit) int { + power := 0 + for _, unit := range units { + if unit.Rank == RankArtillery { + power += 10 + } + if unit.Rank == RankCavalry { + power += 5 + } + if unit.Rank == RankInfantry { + power += 1 + } + } + return power +} diff --git a/internal/routing/models.go b/internal/routing/models.go new file mode 100644 index 0000000..5f85551 --- /dev/null +++ b/internal/routing/models.go @@ -0,0 +1,13 @@ +package routing + +import "time" + +type PlayingState struct { + IsPaused bool +} + +type GameLog struct { + CurrentTime time.Time + Message string + Username string +} diff --git a/internal/routing/routing.go b/internal/routing/routing.go new file mode 100644 index 0000000..f309499 --- /dev/null +++ b/internal/routing/routing.go @@ -0,0 +1,16 @@ +package routing + +const ( + ArmyMovesPrefix = "army_moves" + + WarRecognitionsPrefix = "war" + + PausePrefix = "pause" + + GameLogSlug = "game_logs" +) + +const ( + ExchangePerilDirect = "peril_direct" + ExchangePerilTopic = "peril_topic" +) diff --git a/rabbit.sh b/rabbit.sh new file mode 100755 index 0000000..b09987b --- /dev/null +++ b/rabbit.sh @@ -0,0 +1 @@ +docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management From 3d76ee902c852d14ddee96cb3394bd97ad2a5923 Mon Sep 17 00:00:00 2001 From: Lane Wagner Date: Tue, 2 Apr 2024 12:51:49 -0600 Subject: [PATCH 2/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92e5ceb..c0ba945 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This is the starter code used in Boot.dev's [Learn Pub/Sub](https://learn.boot.d Make sure you have a RabbitMQ server running locally: ```bash -./rabbitmq.sh +./rabbit.sh ``` Using separate terminal windows, you can run clients and servers: From ac6373af6f9f815c093590cf2ea939c93f94efc5 Mon Sep 17 00:00:00 2001 From: wagslane Date: Tue, 2 Apr 2024 15:37:46 -0600 Subject: [PATCH 3/7] pause key --- internal/routing/routing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/routing/routing.go b/internal/routing/routing.go index f309499..6784374 100644 --- a/internal/routing/routing.go +++ b/internal/routing/routing.go @@ -5,7 +5,7 @@ const ( WarRecognitionsPrefix = "war" - PausePrefix = "pause" + PauseKey = "pause" GameLogSlug = "game_logs" ) From 3489a1bda563ff13eacba7efddccb1c161671240 Mon Sep 17 00:00:00 2001 From: wagslane Date: Thu, 4 Apr 2024 11:05:18 -0600 Subject: [PATCH 4/7] movemsg --- internal/gamelogic/move.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gamelogic/move.go b/internal/gamelogic/move.go index 6607d5d..938efe1 100644 --- a/internal/gamelogic/move.go +++ b/internal/gamelogic/move.go @@ -85,6 +85,6 @@ func (gs *GameState) CommandMove(words []string) (ArmyMove, error) { Units: gs.getUnitsSnap(), Player: gs.GetPlayerSnap(), } - + fmt.Printf("Moved %v units to %s\n", len(mv.Units), mv.ToLocation) return mv, nil } From e865803858bed4d8b7cf566b89f2dc14a10df381 Mon Sep 17 00:00:00 2001 From: wagslane Date: Wed, 17 Apr 2024 11:12:18 -0600 Subject: [PATCH 5/7] update started code --- README.md | 27 +-------------------------- internal/gamelogic/logs.go | 3 +++ multiserver.sh | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 multiserver.sh diff --git a/README.md b/README.md index c0ba945..071a206 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,3 @@ # learn-pub-sub-starter (Peril) -This is the starter code used in Boot.dev's [Learn Pub/Sub](https://learn.boot.dev/learn-pub-sub) course. It contains: - -* The `internal/gamelogic` package, which contains the game logic for the Peril game. -* The `internal/routing` package, which contains some routing constants for the game. -* Stubs of the `cmd/client` and `cmd/server` packages, which are `main` packages that run the client and server for the game. -* The `rabbitmq.sh` script, which starts a RabbitMQ server in a Docker container. - -## Running the Game - -Make sure you have a RabbitMQ server running locally: - -```bash -./rabbit.sh -``` - -Using separate terminal windows, you can run clients and servers: - -```bash -go run ./cmd/server -``` - -```bash -go run ./cmd/client -``` - -You will be working in this repository and editing it's files throughout the course. You can peek the solution at any time in the course to make sure you're on the right track. +This is the starter code used in Boot.dev's [Learn Pub/Sub](https://learn.boot.dev/learn-pub-sub) course. diff --git a/internal/gamelogic/logs.go b/internal/gamelogic/logs.go index df40dc0..f9af79f 100644 --- a/internal/gamelogic/logs.go +++ b/internal/gamelogic/logs.go @@ -11,8 +11,11 @@ import ( const logsFile = "game.log" +const writeToDiskSleep = 1 * time.Second + func WriteLog(gamelog routing.GameLog) error { log.Printf("received game log...") + time.Sleep(writeToDiskSleep) f, err := os.OpenFile(logsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { diff --git a/multiserver.sh b/multiserver.sh new file mode 100644 index 0000000..8b785b9 --- /dev/null +++ b/multiserver.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Check if the number of instances was provided +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +num_instances=$1 + +# Array to store process IDs +declare -a pids + +# Function to kill all processes when Ctrl+C is pressed +cleanup() { + echo "Terminating all instances of ./cmd/server..." + for pid in "${pids[@]}"; do + kill -SIGTERM "$pid" + done + exit +} + +# Setup trap for SIGINT +trap 'cleanup' SIGINT + +# Start the specified number of instances of the program in the background +for (( i=0; i Date: Thu, 18 Apr 2024 13:59:29 -0600 Subject: [PATCH 6/7] betterabbit --- rabbit.sh | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rabbit.sh b/rabbit.sh index b09987b..942de18 100755 --- a/rabbit.sh +++ b/rabbit.sh @@ -1 +1,19 @@ -docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management +#!/bin/bash + +case "$1" in + start) + echo "Starting RabbitMQ container..." + docker run -d --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management + ;; + stop) + echo "Stopping RabbitMQ container..." + docker stop rabbitmq + ;; + logs) + echo "Fetching logs for RabbitMQ container..." + docker logs -f rabbitmq + ;; + *) + echo "Usage: $0 {start|stop|logs}" + exit 1 +esac From a358cc0d0da90f4b89c850293c6b74c07cf3e56a Mon Sep 17 00:00:00 2001 From: Fakhri A Date: Tue, 5 Nov 2024 12:49:44 +0700 Subject: [PATCH 7/7] Update move.go, case consistency change `MoveOutComeSafe` to `MoveOutcomeSafe` for case consistency --- internal/gamelogic/move.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/gamelogic/move.go b/internal/gamelogic/move.go index 938efe1..02497c0 100644 --- a/internal/gamelogic/move.go +++ b/internal/gamelogic/move.go @@ -10,7 +10,7 @@ type MoveOutcome int const ( MoveOutcomeSamePlayer MoveOutcome = iota - MoveOutComeSafe + MoveOutcomeSafe MoveOutcomeMakeWar ) @@ -35,7 +35,7 @@ func (gs *GameState) HandleMove(move ArmyMove) MoveOutcome { return MoveOutcomeMakeWar } fmt.Printf("You are safe from %s's units.\n", move.Player.Username) - return MoveOutComeSafe + return MoveOutcomeSafe } func getOverlappingLocation(p1 Player, p2 Player) Location {