starter repo
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.log
|
||||||
@@ -1 +1,3 @@
|
|||||||
# learn-pub-sub-starter
|
# 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.
|
||||||
|
|||||||
7
cmd/client/main.go
Normal file
7
cmd/client/main.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Starting Peril client...")
|
||||||
|
}
|
||||||
7
cmd/server/main.go
Normal file
7
cmd/server/main.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Starting Peril server...")
|
||||||
|
}
|
||||||
3
go.mod
Normal file
3
go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module github.com/bootdotdev/learn-pub-sub-starter
|
||||||
|
|
||||||
|
go 1.22.1
|
||||||
52
internal/gamelogic/gamedata.go
Normal file
52
internal/gamelogic/gamedata.go
Normal file
@@ -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": {},
|
||||||
|
}
|
||||||
|
}
|
||||||
92
internal/gamelogic/gamelogic.go
Normal file
92
internal/gamelogic/gamelogic.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package gamelogic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrintClientHelp() {
|
||||||
|
fmt.Println("Possible commands:")
|
||||||
|
fmt.Println("* move <location> <unitID> <unitID> <unitID>...")
|
||||||
|
fmt.Println(" example:")
|
||||||
|
fmt.Println(" move asia 1")
|
||||||
|
fmt.Println("* spawn <location> <rank>")
|
||||||
|
fmt.Println(" example:")
|
||||||
|
fmt.Println(" spawn europe infantry")
|
||||||
|
fmt.Println("* status")
|
||||||
|
fmt.Println("* spam <n>")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
96
internal/gamelogic/gamestate.go
Normal file
96
internal/gamelogic/gamestate.go
Normal file
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
32
internal/gamelogic/logs.go
Normal file
32
internal/gamelogic/logs.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package gamelogic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bootdotdev/learn-pub-sub-starter/internal/routing"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
92
internal/gamelogic/move.go
Normal file
92
internal/gamelogic/move.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
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 <location> <unitID> <unitID> <unitID> 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
newUnits := []Unit{}
|
||||||
|
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)
|
||||||
|
newUnits = append(newUnits, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
mv := ArmyMove{
|
||||||
|
ToLocation: newLocation,
|
||||||
|
Units: newUnits,
|
||||||
|
Player: gs.GetPlayerSnap(),
|
||||||
|
}
|
||||||
|
fmt.Printf("Moved %v units to %s\n", len(mv.Units), mv.ToLocation)
|
||||||
|
return mv, nil
|
||||||
|
}
|
||||||
19
internal/gamelogic/pause.go
Normal file
19
internal/gamelogic/pause.go
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
34
internal/gamelogic/spawn.go
Normal file
34
internal/gamelogic/spawn.go
Normal file
@@ -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 <location> <rank>")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
105
internal/gamelogic/war.go
Normal file
105
internal/gamelogic/war.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
13
internal/routing/models.go
Normal file
13
internal/routing/models.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type PlayingState struct {
|
||||||
|
IsPaused bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameLog struct {
|
||||||
|
CurrentTime time.Time
|
||||||
|
Message string
|
||||||
|
Username string
|
||||||
|
}
|
||||||
16
internal/routing/routing.go
Normal file
16
internal/routing/routing.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
const (
|
||||||
|
ArmyMovesPrefix = "army_moves"
|
||||||
|
|
||||||
|
WarRecognitionsPrefix = "war"
|
||||||
|
|
||||||
|
PauseKey = "pause"
|
||||||
|
|
||||||
|
GameLogSlug = "game_logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExchangePerilDirect = "peril_direct"
|
||||||
|
ExchangePerilTopic = "peril_topic"
|
||||||
|
)
|
||||||
33
multiserver.sh
Normal file
33
multiserver.sh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if the number of instances was provided
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $0 <number-of-instances>"
|
||||||
|
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<num_instances; i++ )); do
|
||||||
|
go run ./cmd/server &
|
||||||
|
pids+=($!)
|
||||||
|
done
|
||||||
|
|
||||||
|
# Wait for all background processes to finish
|
||||||
|
wait
|
||||||
19
rabbit.sh
Executable file
19
rabbit.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/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
|
||||||
Reference in New Issue
Block a user