]> Skullheadx's Git Forge - monopoly-web.git/commitdiff
refactor into Context
authorSkullheadx <admonty1@protonmail.com>
Tue, 19 May 2026 05:51:37 +0000 (01:51 -0400)
committerSkullheadx <admonty1@protonmail.com>
Tue, 19 May 2026 05:51:50 +0000 (01:51 -0400)
16 files changed:
game/chance.go
game/chest.go
game/color.go
game/config.go [new file with mode: 0644]
game/game.go
game/go.go
game/helpers.go
game/jail.go
game/movement.go
game/railroad.go
game/tax.go
game/todo [new file with mode: 0644]
game/types.go
game/unowned.go [new file with mode: 0644]
game/utility.go
main.go

index 5874a101675cef75d518faab737ff4540322fc9d..bda1a54ffda239a395f8abc62fe98dec4438c12a 100644 (file)
@@ -1,93 +1,83 @@
 package game
 
-var ChanceCards = [...]string{
-       0:  "Advance to St. Charles Place. If you pass Go, collect $200.",
-       1:  "Advance to the nearest Railroad. If unowned, you may buy it from the Bank. If owned, pay owner twice the rental to which they are otherwise entitled.",
-       2:  "Advance to the nearest Railroad. If unowned, you may buy it from the Bank. If owned, pay owner twice the rental to which they are otherwise entitled.",
-       3:  "Advance token to nearest Utility. If unowned, you may buy it from the Bank. If owned, throw dice and pay owner a total ten times amount thrown.",
-       4:  "Go to Jail. Go directly to Jail, do not pass Go, do not collect $200.",
-       5:  "Take a trip to Reading Railroad. If you pass Go, collect $200.",
-       6:  "Advance to Go (Collect $200)",
-       7:  "Get Out of Jail Free.",
-       8:  "Advance to Boardwalk.",
-       9:  "Your building loan matures. Collect $150.",
-       10: "Go Back 3 Spaces.",
-       11: "Speeding fine $15.",
-       12: "Advance to Illinois Avenue. If you pass Go, collect $200.",
-       13: "Make general repairs on all your property. For each house pay $25. For each hotel pay $100.",
-       14: "You have been elected Chairman of the Board. Pay each player $50.",
-       15: "Bank pays you dividend of $50.",
-}
-
-func GetPlayerMoveDistance(start int32, dest int32) int32 {
-       distance := dest - start
-       if distance < 0 {
-               distance += int32(len(BoardSpaces))
+func (ctx *Context) getOwnerID(sID SpaceID) PlayerID {
+       for _, oP := range ctx.Properties.Owners {
+               if oP.SpaceID == sID {
+                       return oP.OwnerID
+               }
        }
-       return distance
+       return BankPlayerID
 }
 
-func ProcessChance() {
-       for _, visitorID := range ChanceVisitors {
-               card := RandSrc.IntN(len(ChanceCards))
+func (ctx *Context) ProcessChance() {
+       for _, visitorID := range ctx.Visitors.Chance {
+               card := ctx.Random.IntN(len(ChanceCards))
 
-               currentPos := Users[visitorID].CurrentSpaceID
+               currentPos := ctx.Players.Alive[visitorID.Index()].CurrentSpaceID
 
                switch card {
                case 0:
-                       MoveQueue = append(MoveQueue, GetPlayerMoveDistance(currentPos, StCharlesPlaceSpaceID))
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, GetPlayerMoveDistance(currentPos, SpecialSpaces.StCharlesPlace))
                case 1, 2:
                        for i := range BoardSpaces {
                                offset := int32(i)
-                               next_pos := currentPos + offset
-                               propertyType := BoardSpaces[next_pos]
-                               if propertyType == TypeRailroad {
+                               next_pos := Add(currentPos, offset)
+                               prop := BoardSpaces[next_pos.Index()]
+                               if prop.PropertyType == TypeRailroad {
                                        distance := GetPlayerMoveDistance(currentPos, next_pos)
-                                       if PropertyOwners[SpaceToOwnableProperty[CalculateNextPos(currentPos, distance)]] != visitorID {
-                                               ModifierRailroadRentMultiplier = 2
+
+                                       if ctx.getOwnerID(CalculateNextPos(currentPos, distance)) != visitorID {
+                                               ctx.Turn.Modifier.RailroadRentMultiplier = 2
                                        }
-                                       MoveQueue = append(MoveQueue, distance)
+                                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, distance)
+                                       break
                                }
                        }
                case 3:
                        for i := range BoardSpaces {
                                offset := int32(i)
-                               next_pos := currentPos + offset
-                               propertyType := BoardSpaces[next_pos]
-                               if propertyType == TypeUtility {
+                               next_pos := Add(currentPos, offset)
+                               prop := BoardSpaces[next_pos.Index()]
+                               if prop.PropertyType == TypeUtility {
                                        distance := GetPlayerMoveDistance(currentPos, next_pos)
-                                       if PropertyOwners[SpaceToOwnableProperty[CalculateNextPos(currentPos, distance)]] != visitorID {
-                                               ModifierUtilityForceRentMultiplier = true
+
+                                       if ctx.getOwnerID(CalculateNextPos(currentPos, distance)) != visitorID {
+                                               ctx.Turn.Modifier.UtilityForceRentMultiplier = true
                                        }
-                                       MoveQueue = append(MoveQueue, distance)
+                                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, distance)
+                                       break
                                }
                        }
 
                case 4:
-                       InJailVisitors = append(InJailVisitors, InJailVisitor{visitorID: visitorID, turns: DEFAULT_JAIL_TURNS})
+                       ctx.Visitors.InJail = append(ctx.Visitors.InJail, InJailVisitor{visitorID: visitorID, TurnsLeft: JailDefaultTurns})
                case 5:
-                       MoveQueue = append(MoveQueue, GetPlayerMoveDistance(currentPos, ReadingRailroadSpaceID))
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, GetPlayerMoveDistance(currentPos, SpecialSpaces.ReadingRailroad))
                case 6:
-                       MoveQueue = append(MoveQueue, GetPlayerMoveDistance(currentPos, GoSpaceID))
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, GetPlayerMoveDistance(currentPos, SpecialSpaces.Go))
                case 7:
-                       Users[visitorID].GetOutOfJailCards++
+                       ctx.Players.Alive[ctx.Turn.Current.Index()].GetOutOfJailCards++
                case 8:
-                       MoveQueue = append(MoveQueue, GetPlayerMoveDistance(currentPos, BoardwalkSpaceID))
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, GetPlayerMoveDistance(currentPos, SpecialSpaces.Boardwalk))
                case 9:
-                       AdjustPlayerMoney(visitorID, 150)
+                       ctx.AdjustPlayerMoney(visitorID, 150)
                case 10:
-                       MoveQueue = append(MoveQueue, -3)
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, -3)
                case 11:
-                       AdjustPlayerMoney(visitorID, -15)
+                       ctx.AdjustPlayerMoney(visitorID, -15)
                case 12:
-                       MoveQueue = append(MoveQueue, GetPlayerMoveDistance(currentPos, IllinoisAvenueSpaceID))
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, GetPlayerMoveDistance(currentPos, SpecialSpaces.IllinoisAvenue))
                case 13:
                        var repairCost int32 = 0
-                       for propID, ownerID := range PropertyOwners {
-                               if ownerID == visitorID && OwnablePropertyType[propID] == TypeColor {
-                                       colorID := OwnableToRespProperty[int32(propID)]
+                       for _, prop := range ctx.Properties.Owners {
+                               ownerID := prop.OwnerID
+                               spaceID := prop.SpaceID
+                               space := BoardSpaces[spaceID.Index()]
+
+                               if ownerID == visitorID && space.PropertyType == TypeColor {
+                                       colorID := space.SubIndexID
                                        colorProp := ColorProperties[colorID]
-                                       if colorProp.Houses > MAX_PROP_HOUSES { // its a hotel
+                                       if colorProp.Houses > ColorMaxHouses { // its a hotel
                                                repairCost += 100
                                        } else {
                                                repairCost += colorProp.Houses * 25
@@ -95,19 +85,19 @@ func ProcessChance() {
                                }
                        }
 
-                       AdjustPlayerMoney(visitorID, -repairCost)
+                       ctx.AdjustPlayerMoney(visitorID, -repairCost)
                case 14:
-                       for i := range Users {
-                               pID := int32(i)
+                       for i := range ctx.Players.Alive {
+                               pID := PlayerID{id: int32(i)}
                                if pID == visitorID {
-                                       AdjustPlayerMoney(pID, -50*int32(len(Users)-1))
+                                       ctx.AdjustPlayerMoney(pID, -50*int32(len(ctx.Players.Alive)-1))
                                } else {
-                                       AdjustPlayerMoney(pID, 50)
+                                       ctx.AdjustPlayerMoney(pID, 50)
                                }
 
                        }
                case 15:
-                       AdjustPlayerMoney(visitorID, 50)
+                       ctx.AdjustPlayerMoney(visitorID, 50)
                }
        }
 }
index 1664dc6b2b0ba92b1054e6d11be1b97f4a5fa223..c43289a2db8936c00de10be190af2267f1c6a8b6 100644 (file)
@@ -1,62 +1,47 @@
 package game
 
-var ChestCards = [...]string{
-       0:  "Advance to Go. (Collect $200)",
-       1:  "Bank error in your favor. Collect $200.",
-       2:  "Holiday fund matures. Receive $100.",
-       3:  "Life insurance matures. Collect $100.",
-       4:  "You inherit $100.",
-       5:  "From sale of stock you get $50.",
-       6:  "Income tax refund. Collect $20.",
-       7:  "It is your birthday. Collect $10 from every player.",
-       8:  "You are assessed for street repair. $40 per house. $115 per hotel.",
-       9:  "Pay hospital fees of $100.",
-       10: "Pay school fees of $50.",
-       11: "Doctor’s fee. Pay $50.",
-       12: "Receive $25 consultancy fee.",
-       13: "Get Out of Jail Free.",
-       14: "Go to Jail. Go directly to jail, do not pass Go, do not collect $200.",
-       15: "You have won second prize in a beauty contest. Collect $10.",
-}
-
-func ProcessChest() {
-       for _, visitorID := range ChestVisitors {
-               card := RandSrc.IntN(len(ChanceCards))
+func (ctx *Context) ProcessChest() {
+       for _, visitorID := range ctx.Visitors.Chest {
+               card := ctx.Random.IntN(len(ChanceCards))
 
-               currentPos := Users[visitorID].CurrentSpaceID
+               currentPos := ctx.Players.Alive[visitorID.Index()].CurrentSpaceID
 
                switch card {
                case 0:
-                       MoveQueue = append(MoveQueue, GetPlayerMoveDistance(currentPos, GoSpaceID))
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, GetPlayerMoveDistance(currentPos, SpecialSpaces.Go))
                case 1:
-                       AdjustPlayerMoney(visitorID, 200)
+                       ctx.AdjustPlayerMoney(visitorID, 200)
                case 2:
-                       AdjustPlayerMoney(visitorID, 100)
+                       ctx.AdjustPlayerMoney(visitorID, 100)
                case 3:
-                       AdjustPlayerMoney(visitorID, 100)
+                       ctx.AdjustPlayerMoney(visitorID, 100)
                case 4:
-                       AdjustPlayerMoney(visitorID, 100)
+                       ctx.AdjustPlayerMoney(visitorID, 100)
                case 5:
-                       AdjustPlayerMoney(visitorID, 50)
+                       ctx.AdjustPlayerMoney(visitorID, 50)
                case 6:
-                       AdjustPlayerMoney(visitorID, 20)
+                       ctx.AdjustPlayerMoney(visitorID, 20)
                case 7:
-                       for i := range Users {
-                               pID := int32(i)
+                       for i := range ctx.Players.Alive {
+                               pID := PlayerID{id: int32(i)}
                                if pID == visitorID {
-                                       AdjustPlayerMoney(pID, 10*int32(len(Users)-1))
+                                       ctx.AdjustPlayerMoney(pID, 10*int32(len(ctx.Players.Alive)-1))
                                } else {
-                                       AdjustPlayerMoney(pID, -10)
+                                       ctx.AdjustPlayerMoney(pID, -10)
                                }
 
                        }
                case 8:
                        var repairCost int32 = 0
-                       for propID, ownerID := range PropertyOwners {
-                               if ownerID == visitorID && OwnablePropertyType[propID] == TypeColor {
-                                       colorID := OwnableToRespProperty[int32(propID)]
+                       for _, prop := range ctx.Properties.Owners {
+                               ownerID := prop.OwnerID
+                               spaceID := prop.SpaceID
+                               space := BoardSpaces[spaceID.Index()]
+
+                               if ownerID == visitorID && space.PropertyType == TypeColor {
+                                       colorID := space.SubIndexID
                                        colorProp := ColorProperties[colorID]
-                                       if colorProp.Houses > MAX_PROP_HOUSES { // its a hotel
+                                       if colorProp.Houses > ColorMaxHouses { // its a hotel
                                                repairCost += 100
                                        } else {
                                                repairCost += colorProp.Houses * 25
@@ -64,21 +49,21 @@ func ProcessChest() {
                                }
                        }
 
-                       AdjustPlayerMoney(visitorID, -repairCost)
+                       ctx.AdjustPlayerMoney(visitorID, -repairCost)
                case 9:
-                       AdjustPlayerMoney(visitorID, -100)
+                       ctx.AdjustPlayerMoney(visitorID, -100)
                case 10:
-                       MoveQueue = append(MoveQueue, -50)
+                       ctx.Turn.MoveQueue = append(ctx.Turn.MoveQueue, -50)
                case 11:
-                       AdjustPlayerMoney(visitorID, -50)
+                       ctx.AdjustPlayerMoney(visitorID, -50)
                case 12:
-                       AdjustPlayerMoney(visitorID, 25)
+                       ctx.AdjustPlayerMoney(visitorID, 25)
                case 13:
-                       InJailVisitors = append(InJailVisitors, InJailVisitor{visitorID: visitorID, turns: DEFAULT_JAIL_TURNS})
+                       ctx.Visitors.InJail = append(ctx.Visitors.InJail, InJailVisitor{visitorID: visitorID, TurnsLeft: JailDefaultTurns})
                case 14:
-                       Users[visitorID].GetOutOfJailCards++
+                       ctx.Players.Alive[ctx.Turn.Current.Index()].GetOutOfJailCards++
                case 15:
-                       AdjustPlayerMoney(visitorID, 10)
+                       ctx.AdjustPlayerMoney(visitorID, 10)
                }
        }
 }
index c49c55ad228811700f9be105437e889c6032ec26..e4b8678b522055d3aa09a5d1c3a2b689b069b597 100644 (file)
@@ -1,20 +1,36 @@
 package game
 
-const MAX_PROP_HOUSES = 4
-
-func HasColorMonopoly(playerID int32, targetGroup ColorGroup) bool {
+func (ctx *Context) HasColorMonopoly(playerID PlayerID, targetGroup ColorGroup) bool {
        var ownedCount int32
-       for propID, ownerID := range PropertyOwners {
-               if ownerID == playerID && OwnablePropertyType[int32(propID)] == TypeColor && ColorProperties[OwnableToRespProperty[int32(propID)]].GroupID == targetGroup {
-                       ownedCount++
+       for _, prop := range ctx.Properties.Owners {
+               ownerID := prop.OwnerID
+
+               if ownerID != playerID {
+                       continue
+               }
+
+               spaceID := prop.SpaceID
+               space := BoardSpaces[spaceID.Index()]
+
+               propType := space.PropertyType
+               if propType != TypeColor {
+                       continue
+               }
+
+               colorID := space.SubIndexID
+               if ColorProperties[colorID].GroupID != targetGroup {
+                       continue
                }
+
+               // the property belongs to player, is a color property and has the right color
+               ownedCount++
        }
 
        return ownedCount == ColorGroupSizes[targetGroup]
 }
 
-func ProcessOwnedColors() {
-       for _, oCV := range OwnedColorVisitors {
+func (ctx *Context) ProcessOwnedColors() {
+       for _, oCV := range ctx.Visitors.Color {
                visitorID := oCV.visitorID
                ownerID := oCV.ownerID
                colorID := oCV.colorID
@@ -24,11 +40,11 @@ func ProcessOwnedColors() {
 
                var rent int32 = prices[prop.Houses]
 
-               if prop.Houses == 0 && HasColorMonopoly(ownerID, prop.GroupID) {
+               if prop.Houses == 0 && ctx.HasColorMonopoly(ownerID, prop.GroupID) {
                        rent *= 2
                }
 
-               AdjustPlayerMoney(visitorID, -rent)
-               AdjustPlayerMoney(ownerID, rent)
+               ctx.AdjustPlayerMoney(visitorID, -rent)
+               ctx.AdjustPlayerMoney(ownerID, rent)
        }
 }
diff --git a/game/config.go b/game/config.go
new file mode 100644 (file)
index 0000000..16fcf9d
--- /dev/null
@@ -0,0 +1,247 @@
+package game
+
+const (
+       StartingDiceRolls int32 = 1
+)
+
+// Board config
+const (
+       TypeGo PropertyType = iota
+       TypeColor
+       TypeChest
+       TypeTax
+       TypeRailroad
+       TypeChance
+       TypeJail
+       TypeUtility
+       TypeParking
+       TypePolice
+)
+
+const (
+       GroupBrown ColorGroup = iota
+       GroupLightBlue
+       GroupPink
+       GroupOrange
+       GroupRed
+       GroupYellow
+       GroupGreen
+       GroupDarkBlue
+)
+
+var ColorGroupSizes = [...]int32{
+       GroupBrown:     2,
+       GroupLightBlue: 3,
+       GroupPink:      3,
+       GroupOrange:    3,
+       GroupRed:       3,
+       GroupYellow:    3,
+       GroupGreen:     3,
+       GroupDarkBlue:  2,
+}
+
+var BoardSpaces = [...]Space{
+       Space{PropertyType: TypeGo, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 0},
+       Space{PropertyType: TypeChest, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 1},
+       Space{PropertyType: TypeTax, SubIndexID: 0},
+       Space{PropertyType: TypeRailroad, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 2},
+       Space{PropertyType: TypeChance, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 3},
+       Space{PropertyType: TypeColor, SubIndexID: 4},
+       Space{PropertyType: TypeJail, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 5},
+       Space{PropertyType: TypeUtility, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 6},
+       Space{PropertyType: TypeColor, SubIndexID: 7},
+       Space{PropertyType: TypeRailroad, SubIndexID: 1},
+       Space{PropertyType: TypeColor, SubIndexID: 8},
+       Space{PropertyType: TypeChest, SubIndexID: 1},
+       Space{PropertyType: TypeColor, SubIndexID: 9},
+       Space{PropertyType: TypeColor, SubIndexID: 10},
+       Space{PropertyType: TypeParking, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 11},
+       Space{PropertyType: TypeChance, SubIndexID: 1},
+       Space{PropertyType: TypeColor, SubIndexID: 12},
+       Space{PropertyType: TypeColor, SubIndexID: 13},
+       Space{PropertyType: TypeRailroad, SubIndexID: 2},
+       Space{PropertyType: TypeColor, SubIndexID: 14},
+       Space{PropertyType: TypeColor, SubIndexID: 15},
+       Space{PropertyType: TypeUtility, SubIndexID: 1},
+       Space{PropertyType: TypeColor, SubIndexID: 16},
+       Space{PropertyType: TypePolice, SubIndexID: 0},
+       Space{PropertyType: TypeColor, SubIndexID: 17},
+       Space{PropertyType: TypeColor, SubIndexID: 18},
+       Space{PropertyType: TypeChest, SubIndexID: 2},
+       Space{PropertyType: TypeColor, SubIndexID: 19},
+       Space{PropertyType: TypeRailroad, SubIndexID: 3},
+       Space{PropertyType: TypeChance, SubIndexID: 2},
+       Space{PropertyType: TypeColor, SubIndexID: 20},
+       Space{PropertyType: TypeTax, SubIndexID: 1},
+       Space{PropertyType: TypeColor, SubIndexID: 21},
+}
+
+const BoardSpacesLen = len(BoardSpaces)
+
+// Color Config
+var ColorProperties = []ColorProperty{
+       {GroupID: GroupBrown, Houses: 0, Name: "Mediterranean Avenue", Price: 60},
+       {GroupID: GroupBrown, Houses: 0, Name: "Baltic Avenue", Price: 60},
+       {GroupID: GroupLightBlue, Houses: 0, Name: "Oriental Avenue", Price: 100},
+       {GroupID: GroupLightBlue, Houses: 0, Name: "Vermont Avenue", Price: 100},
+       {GroupID: GroupLightBlue, Houses: 0, Name: "Connecticut Avenue", Price: 120},
+       {GroupID: GroupPink, Houses: 0, Name: "St. Charles Place", Price: 140},
+       {GroupID: GroupPink, Houses: 0, Name: "States Avenue", Price: 140},
+       {GroupID: GroupPink, Houses: 0, Name: "Virginia Avenue", Price: 160},
+       {GroupID: GroupOrange, Houses: 0, Name: "St. James Place", Price: 180},
+       {GroupID: GroupOrange, Houses: 0, Name: "Tennessee Avenue", Price: 180},
+       {GroupID: GroupOrange, Houses: 0, Name: "New York Avenue", Price: 200},
+       {GroupID: GroupRed, Houses: 0, Name: "Kentucky Avenue", Price: 220},
+       {GroupID: GroupRed, Houses: 0, Name: "Indiana Avenue", Price: 220},
+       {GroupID: GroupRed, Houses: 0, Name: "Illinois Avenue", Price: 240},
+       {GroupID: GroupYellow, Houses: 0, Name: "Atlantic Avenue", Price: 260},
+       {GroupID: GroupYellow, Houses: 0, Name: "Ventnor Avenue", Price: 260},
+       {GroupID: GroupYellow, Houses: 0, Name: "Marvin Gardens", Price: 280},
+       {GroupID: GroupGreen, Houses: 0, Name: "Pacific Avenue", Price: 300},
+       {GroupID: GroupGreen, Houses: 0, Name: "North Carolina Avenue", Price: 300},
+       {GroupID: GroupGreen, Houses: 0, Name: "Pennsylvania Avenue", Price: 320},
+       {GroupID: GroupDarkBlue, Houses: 0, Name: "Park Place", Price: 350},
+       {GroupID: GroupDarkBlue, Houses: 0, Name: "Boardwalk", Price: 400},
+}
+
+var ColorPropertyRents = [][]int32{
+       {2, 10, 30, 90, 160, 250},
+       {4, 20, 60, 180, 320, 450},
+       {6, 30, 90, 270, 400, 550},
+       {6, 30, 90, 270, 400, 550},
+       {8, 40, 100, 300, 450, 600},
+       {10, 50, 150, 450, 625, 750},
+       {10, 50, 150, 450, 625, 750},
+       {12, 60, 180, 500, 700, 900},
+       {14, 70, 200, 550, 750, 950},
+       {14, 70, 200, 550, 750, 950},
+       {16, 80, 220, 600, 800, 1000},
+       {18, 90, 250, 700, 875, 1050},
+       {18, 90, 250, 700, 875, 1050},
+       {20, 100, 300, 750, 925, 1100},
+       {22, 110, 330, 800, 975, 1150},
+       {22, 110, 330, 800, 975, 1150},
+       {24, 120, 360, 850, 1025, 1200},
+       {26, 130, 390, 900, 1100, 1275},
+       {26, 130, 390, 900, 1100, 1275},
+       {28, 150, 450, 1000, 1200, 1400},
+       {35, 175, 500, 1100, 1300, 1500},
+       {50, 200, 600, 1400, 1700, 2000},
+}
+
+// Railroad Config
+var RailroadProperties = []PropertyStatic{
+       {Name: "Reading Railroad", Price: 200},
+       {Name: "Pennsylvania Railroad", Price: 200},
+       {Name: "B.&O. Railroad", Price: 200},
+       {Name: "Short Line", Price: 200},
+}
+
+const RailroadPrice int32 = 200
+
+var RailroadRent = [...]int32{25, 50, 100, 200}
+
+const RailroadMortgageValue int32 = 100
+
+// Utility Config
+var UtilityProperties = []PropertyStatic{
+       {Name: "Electric Company", Price: 150},
+       {Name: "Waterworks", Price: 150},
+}
+
+const UtilityPrice int32 = 150
+
+var UtilityRentMult = [...]int32{4, 10}
+
+const UtilityMortgageValue int32 = 75
+
+// Tax Config
+var TaxSpaces = []TaxSpace{
+       {Name: "Income Tax", Amount: 200},
+       {Name: "Luxury Tax", Amount: 100},
+}
+
+var BankPlayerID PlayerID = PlayerID{id: -1}
+
+var SpecialSpaces ChanceSpaceIDs
+
+func init() {
+       for i, s := range BoardSpaces {
+               spaceID := SpaceID{id: int32(i)}
+
+               propertyType := s.PropertyType
+
+               switch propertyType {
+               case TypeColor:
+                       switch ColorProperties[s.SubIndexID].Name {
+                       case "St. Charles Place":
+                               SpecialSpaces.StCharlesPlace = spaceID
+                       case "Boardwalk":
+                               SpecialSpaces.Boardwalk = spaceID
+                       case "Illinois Avenue":
+                               SpecialSpaces.IllinoisAvenue = spaceID
+                       }
+               case TypeRailroad:
+                       if RailroadProperties[s.SubIndexID].Name == "Reading Railroad" {
+                               SpecialSpaces.ReadingRailroad = spaceID
+                       }
+               case TypeGo:
+                       SpecialSpaces.Go = spaceID
+               }
+       }
+}
+
+const JailDefaultTurns int32 = 3
+const JailBuyoutCost int32 = 50
+
+const GoSalary int32 = 200
+
+const ColorMaxHouses = 4
+
+const StartingMoney int32 = 1500
+const StartingGetOutOfJailFreeCards int32 = 0
+
+var ChestCards = [...]string{
+       0:  "Advance to Go. (Collect $200)",
+       1:  "Bank error in your favor. Collect $200.",
+       2:  "Holiday fund matures. Receive $100.",
+       3:  "Life insurance matures. Collect $100.",
+       4:  "You inherit $100.",
+       5:  "From sale of stock you get $50.",
+       6:  "Income tax refund. Collect $20.",
+       7:  "It is your birthday. Collect $10 from every player.",
+       8:  "You are assessed for street repair. $40 per house. $115 per hotel.",
+       9:  "Pay hospital fees of $100.",
+       10: "Pay school fees of $50.",
+       11: "Doctor’s fee. Pay $50.",
+       12: "Receive $25 consultancy fee.",
+       13: "Get Out of Jail Free.",
+       14: "Go to Jail. Go directly to jail, do not pass Go, do not collect $200.",
+       15: "You have won second prize in a beauty contest. Collect $10.",
+}
+
+var ChanceCards = [...]string{
+       0:  "Advance to St. Charles Place. If you pass Go, collect $200.",
+       1:  "Advance to the nearest Railroad. If unowned, you may buy it from the Bank. If owned, pay owner twice the rental to which they are otherwise entitled.",
+       2:  "Advance to the nearest Railroad. If unowned, you may buy it from the Bank. If owned, pay owner twice the rental to which they are otherwise entitled.",
+       3:  "Advance token to nearest Utility. If unowned, you may buy it from the Bank. If owned, throw dice and pay owner a total ten times amount thrown.",
+       4:  "Go to Jail. Go directly to Jail, do not pass Go, do not collect $200.",
+       5:  "Take a trip to Reading Railroad. If you pass Go, collect $200.",
+       6:  "Advance to Go (Collect $200)",
+       7:  "Get Out of Jail Free.",
+       8:  "Advance to Boardwalk.",
+       9:  "Your building loan matures. Collect $150.",
+       10: "Go Back 3 Spaces.",
+       11: "Speeding fine $15.",
+       12: "Advance to Illinois Avenue. If you pass Go, collect $200.",
+       13: "Make general repairs on all your property. For each house pay $25. For each hotel pay $100.",
+       14: "You have been elected Chairman of the Board. Pay each player $50.",
+       15: "Bank pays you dividend of $50.",
+}
index e147eb460139abe89c7a2c08e490d30580b811e4..2c678386980b5a996f4d38d70c0d39a409d01855 100644 (file)
@@ -4,92 +4,175 @@ import (
        "math/rand/v2"
 )
 
-var RandSeed = rand.NewPCG(20, 26)
-var RandSrc = rand.New(RandSeed)
+func initTurn(pID PlayerID) Turn {
+       return Turn{
+               Current:            pID,
+               Ended:              false,
+               DiceRollsRemaining: StartingDiceRolls,
+               NumDiceRolled:      0,
+               RolledDoubles:      false,
+               MoveQueue:          []int32{},
+               InDebt:             false,
+               Modifier: Modifiers{
+                       RailroadRentMultiplier:     1,
+                       UtilityForceRentMultiplier: false,
+               },
+       }
+}
 
-var Users []User
-var DebtEvents []int32
-var MoveablePlayers []int32
-var MoveQueue []int32
+func InitCtx(randSeed rand.Source, players []Player) *Context {
+       startingPlayerID := PlayerID{id: 0}
+       ownableProps := []OwnableProperty{}
+       for i, s := range BoardSpaces {
+               spaceID := SpaceID{id: int32(i)}
+
+               propertyType := s.PropertyType
+               if propertyType == TypeColor || propertyType == TypeRailroad || propertyType == TypeUtility {
+                       ownableProps = append(ownableProps, OwnableProperty{
+                               OwnerID: BankPlayerID,
+                               SpaceID: spaceID,
+                       })
+               }
+       }
+
+       return &Context{
+               Random: rand.New(randSeed),
+               Players: Players{
+                       Alive: players,
+               },
+               Turn: initTurn(startingPlayerID),
+               Visitors: Visitors{
+                       Unowned:  []UnownedPropertyVisitor{},
+                       Color:    []OwnedColorVisitor{},
+                       Railroad: []OwnedRailroadVisitor{},
+                       Utility:  []OwnedUtilityVisitor{},
+                       Go:       []PlayerID{},
+                       Tax:      []TaxVisitor{},
+                       Chance:   []PlayerID{},
+                       Chest:    []PlayerID{},
+                       InJail:   []InJailVisitor{},
+                       // Parking:  []PlayerID{},
+                       // Police:   []PlayerID{},
+               },
+               Properties: Properties{
+                       Owners:    ownableProps,
+                       Mortgages: []PropertyID{},
+               },
+       }
+}
 
-var ModifierRailroadRentMultiplier int32 = 1
-var ModifierUtilityForceRentMultiplier bool = false
+func InitPlayer() Player {
+       return Player{
+               UUID:              "abc", // TODO: Generate proper UUID
+               Money:             StartingMoney,
+               CurrentSpaceID:    SpecialSpaces.Go,
+               GetOutOfJailCards: StartingGetOutOfJailFreeCards,
+               CanMove:           true,
+       }
+}
 
-var TurnPlayerID int32 = 0
-var TurnEndedSignal bool = false
-var DiceRollsRemaining int32 = 1
-var numDiceRolled int32 = 0
-var RolledDoubles bool = false
+func (ctx *Context) GetCurrentTurnPlayer() *Player {
+       return &ctx.Players.Alive[ctx.Turn.Current.Index()]
+}
 
-func ValidateCanRoll(UUID string) bool {
-       if Users[TurnPlayerID].UUID == UUID && DiceRollsRemaining > 0 {
+func (ctx *Context) ValidateIsTurn(UUID string) bool {
+       if ctx.GetCurrentTurnPlayer().UUID == UUID {
                return true
        }
+       return false
+}
 
+func (ctx *Context) ValidateCanRoll(UUID string) bool {
+       if ctx.ValidateIsTurn(UUID) && ctx.Turn.DiceRollsRemaining > 0 {
+               return true
+       }
        return false
 }
 
-func ValidateCanEndTurn(UUID string) bool {
-       if Users[TurnPlayerID].UUID != UUID || DiceRollsRemaining > 0 || Users[TurnPlayerID].Money < 0 {
-               return false
+func (ctx *Context) ValidateCanEndTurn(UUID string) bool {
+       if !(ctx.ValidateIsTurn(UUID) || ctx.Turn.DiceRollsRemaining > 0 || ctx.GetCurrentTurnPlayer().Money < 0) {
+               return true
        }
-       return true
+       return false
 }
 
-func ValidateCanExitJail(UUID string) bool {
-       for _, iJV := range InJailVisitors {
-               player := Users[iJV.visitorID]
-               if Users[TurnPlayerID].UUID == UUID && player.UUID == UUID {
+func (ctx *Context) ValidateCanExitJail(UUID string) bool {
+       for _, iJV := range ctx.Visitors.InJail {
+               if ctx.Players.Alive[iJV.visitorID.Index()].UUID == UUID {
                        return true
                }
        }
        return false
 }
 
-func ProcessLanding() {
-       ProcessGo()
-       ProcessTax()
+func (ctx *Context) ProcessLanding() {
+       ctx.ProcessGo()
+       ctx.ProcessTax()
 
-       ProcessOwnedColors()
-       ProcessOwnedUtility()
-       ProcessOwnedRailroad()
+       ctx.ProcessOwnedColors()
+       ctx.ProcessOwnedUtility()
+       ctx.ProcessOwnedRailroad()
+       ctx.ProcessUnowned()
 
-       ProcessChance()
-       ProcessChest()
+       ctx.ProcessChance()
+       ctx.ProcessChest()
        // ProcessPolice()
 
-       ProcessJail()
+       ctx.ProcessJail()
 }
 
-func RollDice() {
+func (ctx *Context) RollDice() {
        // Roll Dice
-       diceRoll1 := RandSrc.Int32N(6) + 1
-       diceRoll2 := RandSrc.Int32N(6) + 1
+       diceRoll1 := ctx.Random.Int32N(6) + 1
+       diceRoll2 := ctx.Random.Int32N(6) + 1
 
-       numDiceRolled++
+       ctx.Turn.NumDiceRolled++
+       ctx.Turn.DiceRollsRemaining--
 
        if diceRoll1 == diceRoll2 {
-               RolledDoubles = true
-               DiceRollsRemaining++
-               RemovePlayerFromJail(TurnPlayerID)
+               ctx.Turn.RolledDoubles = true
+               ctx.Turn.DiceRollsRemaining++
+               ctx.RemovePlayerFromJail(ctx.Turn.Current)
        }
 
-       if numDiceRolled >= 3 {
-               InJailVisitors = append(InJailVisitors, InJailVisitor{visitorID: TurnPlayerID, turns: DEFAULT_JAIL_TURNS})
+       if ctx.Turn.NumDiceRolled >= 3 {
+               ctx.Visitors.InJail = append(ctx.Visitors.InJail, InJailVisitor{visitorID: ctx.Turn.Current, TurnsLeft: JailDefaultTurns})
        }
 
 }
 
-func EndTurn() {
-       // next player's turn
-       TurnPlayerID = (TurnPlayerID + 1) % int32(len(Users))
-       TurnEndedSignal = false
+func (ctx *Context) EndTurn() {
+       nextTurnPlayerID := PlayerID{id: (ctx.Turn.Current.id + 1) % int32(len(ctx.Players.Alive))}
+       ctx.Turn = initTurn(nextTurnPlayerID)
+}
+
+func (ctx *Context) IsMortgaged(propID PropertyID) bool {
+       for _, oPID := range ctx.Properties.Mortgages {
+               if oPID == propID {
+                       return true
+               }
+       }
+       return false
+}
 
-       // reset dice
-       DiceRollsRemaining = 1
-       numDiceRolled = 0
-       RolledDoubles = false
+func (ctx *Context) IsOwned(spaceID SpaceID) bool {
+       for _, prop := range ctx.Properties.Owners {
+               if spaceID == prop.SpaceID {
+                       if prop.OwnerID == BankPlayerID {
+                               return false
+                       } else {
+                               return true
+                       }
+               }
+       }
+       panic("Space is not an ownable property")
+}
 
-       // let player move next turn
-       MoveQueue = MoveQueue[:0]
+func (ctx *Context) getPropID(spaceID SpaceID) PropertyID {
+       for i, prop := range ctx.Properties.Owners {
+               if spaceID == prop.SpaceID {
+                       return PropertyID{id: int32(i)}
+               }
+       }
+       panic("Space is not an ownable property")
 }
index d88505b68be27dda87cdcb958ecf2d12a344de1c..21d8339e7ba460b459990fe76f9b80ec42cef894 100644 (file)
@@ -1,9 +1,7 @@
 package game
 
-const GO_SALARY int32 = 200
-
-func ProcessGo() {
-       for _, playerID := range GoVisitors {
-               AdjustPlayerMoney(playerID, GO_SALARY)
+func (ctx *Context) ProcessGo() {
+       for _, playerID := range ctx.Visitors.Go {
+               ctx.AdjustPlayerMoney(playerID, GoSalary)
        }
 }
index 262d8442122b386dae092805fde485da9106c37c..467e1dd8304673e71b301c09d0ea89b9b7ebc40f 100644 (file)
@@ -1,28 +1,12 @@
 package game
 
-func IsInDebt(playerID int32) (bool, int32) {
-       for i, pID := range DebtEvents {
-               if pID == playerID {
-                       return true, int32(i)
-               }
-       }
-       return false, -1
-}
-
-func AdjustPlayerMoney(playerID int32, amount int32) {
-       Users[playerID].Money += amount
-
-       inDebt, i := IsInDebt(playerID)
+func (ctx *Context) AdjustPlayerMoney(playerID PlayerID, amount int32) {
+       ctx.Players.Alive[playerID.Index()].Money += amount
 
-       if Users[playerID].Money < 0 {
-               if !inDebt {
-                       DebtEvents = append(DebtEvents, playerID)
-               }
+       if ctx.Players.Alive[playerID.Index()].Money < 0 {
+               ctx.Turn.InDebt = true
        } else { // Money >= 0
-               if inDebt { // remove player from DebtEvents table
-                       DebtEvents[i] = DebtEvents[len(DebtEvents)-1]
-                       DebtEvents = DebtEvents[:len(DebtEvents)-1]
-               }
+               ctx.Turn.InDebt = false
        }
 
 }
index b8211a9f5080a6e88eb826e6a6e00f27fe1555b0..f7d02b208c7da507b4142ae7c664f6c09e154e66 100644 (file)
@@ -4,39 +4,36 @@ import (
        "errors"
 )
 
-const DEFAULT_JAIL_TURNS int32 = 3
-const JAIL_BUYOUT_COST int32 = 50
-
-func RemovePlayerFromJail(playerID int32) {
-       for i, iJV := range InJailVisitors {
+func (ctx *Context) RemovePlayerFromJail(playerID PlayerID) {
+       for i, iJV := range ctx.Visitors.InJail {
                if playerID == iJV.visitorID {
-                       InJailVisitors[i] = InJailVisitors[len(InJailVisitors)-1]
-                       InJailVisitors = InJailVisitors[:len(InJailVisitors)-1]
+                       ctx.Visitors.InJail[i] = ctx.Visitors.InJail[len(ctx.Visitors.InJail)-1]
+                       ctx.Visitors.InJail = ctx.Visitors.InJail[:len(ctx.Visitors.InJail)-1]
 
                }
 
        }
-       MoveablePlayers = append(MoveablePlayers, playerID)
+       ctx.Players.Alive[playerID.Index()].CanMove = true
 }
 
-func RemovePlayerFromMoveable(pID int32) {
-       for i, playerID := range MoveablePlayers {
+func (ctx *Context) RemovePlayerFromMoveable(pID PlayerID) {
+       for i, _ := range ctx.Players.Alive {
+               playerID := PlayerID{id: int32(i)}
                if pID == playerID {
-                       MoveablePlayers[i] = MoveablePlayers[len(MoveablePlayers)-1]
-                       MoveablePlayers = MoveablePlayers[:len(MoveablePlayers)-1]
+                       ctx.Players.Alive[playerID.Index()].CanMove = false
                }
        }
 }
 
-func ProcessJail() {
-       for _, iJV := range InJailVisitors {
+func (ctx *Context) ProcessJail() {
+       for _, iJV := range ctx.Visitors.InJail {
                visitorID := iJV.visitorID
-               turns := iJV.turns
+               turnsLeft := iJV.TurnsLeft
 
-               if turns <= 0 {
-                       RemovePlayerFromJail(visitorID)
+               if turnsLeft <= 0 {
+                       ctx.RemovePlayerFromJail(visitorID)
                } else {
-                       RemovePlayerFromMoveable(visitorID)
+                       ctx.RemovePlayerFromMoveable(visitorID)
                }
 
        }
@@ -45,10 +42,11 @@ func ProcessJail() {
 var ErrNotEnoughJailCards = errors.New("Cannot use jail card: player does not have enough get out of jail free cards")
 var ErrNotEnoughMoney = errors.New("Cannot execute action: player does not have enough money")
 
-func JailUseCard() error {
-       if Users[TurnPlayerID].GetOutOfJailCards > 0 {
-               RemovePlayerFromJail(TurnPlayerID)
-               Users[TurnPlayerID].GetOutOfJailCards -= 1
+func (ctx *Context) JailUseCard() error {
+       currID := ctx.Turn.Current
+       if ctx.Players.Alive[currID.Index()].GetOutOfJailCards > 0 {
+               ctx.RemovePlayerFromJail(currID)
+               ctx.Players.Alive[currID.Index()].GetOutOfJailCards -= 1
                return nil
 
        } else {
@@ -56,10 +54,11 @@ func JailUseCard() error {
        }
 }
 
-func JailBuyout() error {
-       if Users[TurnPlayerID].Money >= JAIL_BUYOUT_COST {
-               RemovePlayerFromJail(TurnPlayerID)
-               AdjustPlayerMoney(TurnPlayerID, -JAIL_BUYOUT_COST)
+func (ctx *Context) JailBuyout() error {
+       currID := ctx.Turn.Current
+       if ctx.Players.Alive[currID.Index()].Money >= JailBuyoutCost {
+               ctx.RemovePlayerFromJail(currID)
+               ctx.AdjustPlayerMoney(currID, -JailBuyoutCost)
                return nil
        } else {
                return ErrNotEnoughMoney
index 8c64ec1bc26fd57d6fc73ec790bf8d778e1f19c7..61772a024db9d9bc52213d19a5e3a3e91042ccbe 100644 (file)
 package game
 
-var BoardSpaces = [...]PropertyType{
-       TypeGo,
-       TypeColor,
-       TypeChest,
-       TypeColor,
-       TypeTax,
-       TypeRailroad,
-       TypeColor,
-       TypeChance,
-       TypeColor,
-       TypeColor,
-       TypeJail,
-       TypeColor,
-       TypeUtility,
-       TypeColor,
-       TypeColor,
-       TypeRailroad,
-       TypeColor,
-       TypeChest,
-       TypeColor,
-       TypeColor,
-       TypeParking,
-       TypeColor,
-       TypeChance,
-       TypeColor,
-       TypeColor,
-       TypeRailroad,
-       TypeColor,
-       TypeColor,
-       TypeUtility,
-       TypeColor,
-       TypeJail,
-       TypeColor,
-       TypeColor,
-       TypeChest,
-       TypeColor,
-       TypeRailroad,
-       TypeChance,
-       TypeColor,
-       TypeTax,
-       TypeColor,
-}
-
-type PropertyStatic struct {
-       Name  string
-       Price int32
-}
-
-type ColorGroup int32
-
-const (
-       GroupBrown ColorGroup = iota
-       GroupLightBlue
-       GroupPink
-       GroupOrange
-       GroupRed
-       GroupYellow
-       GroupGreen
-       GroupDarkBlue
-)
-
-var ColorGroupSizes = [...]int32{
-       GroupBrown:     2,
-       GroupLightBlue: 3,
-       GroupPink:      3,
-       GroupOrange:    3,
-       GroupRed:       3,
-       GroupYellow:    3,
-       GroupGreen:     3,
-       GroupDarkBlue:  2,
-}
-
-type ColorProperty struct {
-       Name    string
-       Price   int32
-       GroupID ColorGroup
-       Houses  int32
-}
-
-var ColorProperties = []ColorProperty{
-       {GroupID: GroupBrown, Houses: 0, Name: "Mediterranean Avenue", Price: 60},
-       {GroupID: GroupBrown, Houses: 0, Name: "Baltic Avenue", Price: 60},
-       {GroupID: GroupLightBlue, Houses: 0, Name: "Oriental Avenue", Price: 100},
-       {GroupID: GroupLightBlue, Houses: 0, Name: "Vermont Avenue", Price: 100},
-       {GroupID: GroupLightBlue, Houses: 0, Name: "Connecticut Avenue", Price: 120},
-       {GroupID: GroupPink, Houses: 0, Name: "St. Charles Place", Price: 140},
-       {GroupID: GroupPink, Houses: 0, Name: "States Avenue", Price: 140},
-       {GroupID: GroupPink, Houses: 0, Name: "Virginia Avenue", Price: 160},
-       {GroupID: GroupOrange, Houses: 0, Name: "St. James Place", Price: 180},
-       {GroupID: GroupOrange, Houses: 0, Name: "Tennessee Avenue", Price: 180},
-       {GroupID: GroupOrange, Houses: 0, Name: "New York Avenue", Price: 200},
-       {GroupID: GroupRed, Houses: 0, Name: "Kentucky Avenue", Price: 220},
-       {GroupID: GroupRed, Houses: 0, Name: "Indiana Avenue", Price: 220},
-       {GroupID: GroupRed, Houses: 0, Name: "Illinois Avenue", Price: 240},
-       {GroupID: GroupYellow, Houses: 0, Name: "Atlantic Avenue", Price: 260},
-       {GroupID: GroupYellow, Houses: 0, Name: "Ventnor Avenue", Price: 260},
-       {GroupID: GroupYellow, Houses: 0, Name: "Marvin Gardens", Price: 280},
-       {GroupID: GroupGreen, Houses: 0, Name: "Pacific Avenue", Price: 300},
-       {GroupID: GroupGreen, Houses: 0, Name: "North Carolina Avenue", Price: 300},
-       {GroupID: GroupGreen, Houses: 0, Name: "Pennsylvania Avenue", Price: 320},
-       {GroupID: GroupDarkBlue, Houses: 0, Name: "Park Place", Price: 350},
-       {GroupID: GroupDarkBlue, Houses: 0, Name: "Boardwalk", Price: 400},
-}
-
-var ColorPropertyRents = [][]int32{
-       {2, 10, 30, 90, 160, 250},
-       {4, 20, 60, 180, 320, 450},
-       {6, 30, 90, 270, 400, 550},
-       {6, 30, 90, 270, 400, 550},
-       {8, 40, 100, 300, 450, 600},
-       {10, 50, 150, 450, 625, 750},
-       {10, 50, 150, 450, 625, 750},
-       {12, 60, 180, 500, 700, 900},
-       {14, 70, 200, 550, 750, 950},
-       {14, 70, 200, 550, 750, 950},
-       {16, 80, 220, 600, 800, 1000},
-       {18, 90, 250, 700, 875, 1050},
-       {18, 90, 250, 700, 875, 1050},
-       {20, 100, 300, 750, 925, 1100},
-       {22, 110, 330, 800, 975, 1150},
-       {22, 110, 330, 800, 975, 1150},
-       {24, 120, 360, 850, 1025, 1200},
-       {26, 130, 390, 900, 1100, 1275},
-       {26, 130, 390, 900, 1100, 1275},
-       {28, 150, 450, 1000, 1200, 1400},
-       {35, 175, 500, 1100, 1300, 1500},
-       {50, 200, 600, 1400, 1700, 2000},
-}
-
-var RailroadProperties = []PropertyStatic{
-       {Name: "Reading Railroad", Price: 200},
-       {Name: "Pennsylvania Railroad", Price: 200},
-       {Name: "B.&O. Railroad", Price: 200},
-       {Name: "Short Line", Price: 200},
-}
-
-const RailroadPrice int32 = 200
-
-var RailroadRent = [...]int32{25, 50, 100, 200}
-
-const RailroadMortgageValue int32 = 100
-
-var UtilityProperties = []PropertyStatic{
-       {Name: "Electric Company", Price: 150},
-       {Name: "Waterworks", Price: 150},
-}
-
-const UtilityPrice int32 = 150
-
-var UtilityRentMult = [...]int32{4, 10}
-
-const UtilityMortgageValue int32 = 75
-
-type TaxSpace struct {
-       Name   string
-       Amount int32
-}
-
-var TaxSpaces = []TaxSpace{
-       {Name: "Income Tax", Amount: 200},
-       {Name: "Luxury Tax", Amount: 100},
-}
-
-var PropertyOwners = []int32{}             // playerID
-var OwnablePropertyType = []PropertyType{} // uses ownablePropertyID
-var PropertyMortgages = []int32{}          // mortgaged ownablePropertyIDs
-
-func IsMortgaged(ownablePropertyID int32) bool {
-       for _, oPID := range PropertyMortgages {
-               if oPID == ownablePropertyID {
-                       return true
-               }
-       }
-       return false
-}
-
-var SpaceToRespProperty = make(map[int32]int32)
-var SpaceToOwnableProperty = make(map[int32]int32)
-var SpaceToTaxSpace = make(map[int32]int32)
-
-var OwnableToRespProperty = make(map[int32]int32)
-var RespPropertyToOwnable = make(map[int32]int32)
-
-var StCharlesPlaceSpaceID int32 = 0
-var GoSpaceID int32 = 0
-var ReadingRailroadSpaceID int32 = 0
-var BoardwalkSpaceID int32 = 0
-var IllinoisAvenueSpaceID int32 = 0
-
-func init() {
-       var (
-               colorIndex    int32 = 0
-               railroadIndex int32 = 0
-               utilityIndex  int32 = 0
-               taxIndex      int32 = 0
-               ownableIndex  int32 = 0
-       )
-
-       for i, propertyType := range BoardSpaces {
-               spaceID := int32(i)
-
-               if propertyType == TypeColor || propertyType == TypeRailroad || propertyType == TypeUtility {
-                       SpaceToOwnableProperty[spaceID] = ownableIndex
-                       PropertyOwners = append(PropertyOwners, -1)
-                       OwnablePropertyType = append(OwnablePropertyType, propertyType)
-               }
-
-               switch propertyType {
-               case TypeColor:
-                       SpaceToRespProperty[spaceID] = colorIndex
-                       OwnableToRespProperty[ownableIndex] = colorIndex
-                       RespPropertyToOwnable[colorIndex] = ownableIndex
-
-                       switch ColorProperties[colorIndex].Name {
-                       case "St. Charles Place":
-                               StCharlesPlaceSpaceID = spaceID
-                       case "Boardwalk":
-                               BoardwalkSpaceID = spaceID
-                       case "Illinois Avenue":
-                               IllinoisAvenueSpaceID = spaceID
-                       }
-
-                       colorIndex++
-               case TypeRailroad:
-                       SpaceToRespProperty[spaceID] = railroadIndex
-                       OwnableToRespProperty[ownableIndex] = railroadIndex
-                       RespPropertyToOwnable[railroadIndex] = ownableIndex
-                       if ColorProperties[colorIndex].Name == "Reading Railroad" {
-                               ReadingRailroadSpaceID = spaceID
-                       }
-                       railroadIndex++
-               case TypeUtility:
-                       SpaceToRespProperty[spaceID] = utilityIndex
-                       OwnableToRespProperty[ownableIndex] = utilityIndex
-                       RespPropertyToOwnable[utilityIndex] = ownableIndex
-                       utilityIndex++
-               case TypeTax:
-                       SpaceToTaxSpace[spaceID] = taxIndex
-                       taxIndex++
-               case TypeGo:
-                       GoSpaceID = spaceID
-               }
-
-               ownableIndex++
-
+func GetPlayerMoveDistance(start SpaceID, dest SpaceID) int32 {
+       distance := dest.id - start.id
+       if distance < 0 {
+               distance += int32(len(BoardSpaces))
        }
-
-}
-
-type OwnedColorVisitor struct {
-       visitorID int32
-       ownerID   int32
-       colorID   int32
-}
-
-type OwnedRailroadVisitor struct {
-       visitorID  int32
-       ownerID    int32
-       railroadID int32
-}
-
-type OwnedUtilityVisitor struct {
-       visitorID int32
-       ownerID   int32
-       utilityID int32
-       diceRoll  int32
-}
-
-type UnownedPropertyVisitor struct {
-       visitorID  int32
-       propertyID int32
+       return distance
 }
 
-type InJailVisitor struct {
-       visitorID int32
-       turns     int32
+func (ctx *Context) AllowedToMove(playerID PlayerID) bool {
+       return ctx.Players.Alive[playerID.Index()].CanMove
 }
 
-var (
-       UnownedPropertyVisitors []UnownedPropertyVisitor
-       OwnedColorVisitors      []OwnedColorVisitor
-       OwnedRailroadVisitors   []OwnedRailroadVisitor
-       OwnedUtilityVisitors    []OwnedUtilityVisitor
-       GoVisitors              []int32
-       TaxVisitors             []int32
-       ChanceVisitors          []int32
-       ChestVisitors           []int32
-       InJailVisitors          []InJailVisitor
-       ParkingVisitors         []int32
-       PoliceVisitors          []int32
-)
-
-func AllowedToMove(playerID int32) bool {
-       for _, pID := range MoveablePlayers {
-               if playerID == pID {
-                       return true
-               }
-       }
-       return false
-}
-
-func ProcessMovement() {
-       for i, playerID := range MoveablePlayers {
-               if playerID == TurnPlayerID {
-                       // Movement
-                       for {
-                               // condition to stop moving
-                               if len(MoveQueue) == 0 {
-                                       // player can no longer move
-                                       MoveablePlayers[i] = MoveablePlayers[len(MoveablePlayers)-1]
-                                       MoveablePlayers = MoveablePlayers[:len(MoveablePlayers)-1]
-                                       break
-                               }
-
-                               dist := MoveQueue[0]
-                               MoveQueue = MoveQueue[1:]
-                               player := Users[playerID]
-                               AdvancePlayer(playerID, player.CurrentSpaceID, dist)
-
-                               ProcessLanding()
-                       }
-
-               }
+func (ctx *Context) ProcessMovement() {
+       cID := ctx.Turn.Current
+       if ctx.AllowedToMove(cID) {
+               dist := ctx.Turn.MoveQueue[0]
+               ctx.Turn.MoveQueue = ctx.Turn.MoveQueue[1:]
+               ctx.AdvancePlayer(cID, ctx.Players.Alive[cID.Index()].CurrentSpaceID, dist)
 
+               ctx.ProcessLanding()
        }
 
 }
 
-func CalculateNextPos(currentPosition int32, distance int32) int32 {
-       nextPos := (currentPosition + distance)
-       nextPos %= int32(len(BoardSpaces))
+func CalculateNextPos(currentPosition SpaceID, distance int32) SpaceID {
+       nextPos := Add(currentPosition, distance)
+       nextPos.id %= int32(len(BoardSpaces))
 
        return nextPos
 }
 
-func AdvancePlayer(playerID int32, currentPosition int32, diceRoll int32) {
+func (ctx *Context) AdvancePlayer(playerID PlayerID, currentPosition SpaceID, diceRoll int32) {
        nextPos := CalculateNextPos(currentPosition, diceRoll)
 
-       numGoPasses := (currentPosition + diceRoll) / (int32(len(BoardSpaces)) - 1)
+       numGoPasses := Add(currentPosition, diceRoll).id / int32(len(BoardSpaces))
+
        if numGoPasses > 0 {
+               if BoardSpaces[nextPos.Index()].PropertyType == TypeGo {
+                       numGoPasses--
+               }
                for range numGoPasses {
-                       GoVisitors = append(GoVisitors, playerID)
+                       ctx.Visitors.Go = append(ctx.Visitors.Go, playerID)
                }
        }
 
-       propType := BoardSpaces[nextPos]
+       prop := BoardSpaces[nextPos.Index()]
 
-       switch propType {
+       switch prop.PropertyType {
        case TypeGo:
-               GoVisitors = append(GoVisitors, playerID)
+               ctx.Visitors.Go = append(ctx.Visitors.Go, playerID)
        case TypeChest:
-               ChestVisitors = append(ChestVisitors, playerID)
+               ctx.Visitors.Chest = append(ctx.Visitors.Chest, playerID)
        case TypeChance:
-               ChanceVisitors = append(ChanceVisitors, playerID)
+               ctx.Visitors.Chance = append(ctx.Visitors.Chance, playerID)
        case TypeTax:
-               TaxVisitors = append(TaxVisitors, playerID)
-       // case TypeParking: // nothing ever happens
-       //      ParkingVisitors = append(ParkingVisitors, playerID)
+               ctx.Visitors.Tax = append(ctx.Visitors.Tax, TaxVisitor{visitorID: playerID, taxID: prop.SubIndexID})
        case TypePolice: // hardcoding to send straight to jail
-               InJailVisitors = append(InJailVisitors, InJailVisitor{visitorID: playerID, turns: DEFAULT_JAIL_TURNS})
+               ctx.Visitors.InJail = append(ctx.Visitors.InJail, InJailVisitor{visitorID: playerID, TurnsLeft: JailDefaultTurns})
        case TypeJail:
-               InJailVisitors = append(InJailVisitors, InJailVisitor{visitorID: playerID, turns: DEFAULT_JAIL_TURNS})
+               ctx.Visitors.InJail = append(ctx.Visitors.InJail, InJailVisitor{visitorID: playerID, TurnsLeft: JailDefaultTurns})
        case TypeColor:
-               propIndex := SpaceToOwnableProperty[nextPos]
-               if PropertyOwners[propIndex] != -1 { // property owned?
-                       ownerID := PropertyOwners[propIndex]
-                       if ownerID != playerID && !IsMortgaged(propIndex) { // not by you
-                               OwnedColorVisitors = append(OwnedColorVisitors, OwnedColorVisitor{visitorID: playerID, ownerID: ownerID, colorID: SpaceToRespProperty[nextPos]})
+               propID := ctx.getPropID(nextPos)
+               ownerID := ctx.Properties.Owners[propID.Index()].OwnerID
+               if ownerID != BankPlayerID { // property owned?
+                       if ownerID != playerID && !ctx.IsMortgaged(propID) { // not by you
+                               ctx.Visitors.Color = append(ctx.Visitors.Color, OwnedColorVisitor{visitorID: playerID, ownerID: ownerID, colorID: prop.SubIndexID})
                        }
                } else {
-                       UnownedPropertyVisitors = append(UnownedPropertyVisitors, UnownedPropertyVisitor{visitorID: playerID, propertyID: propIndex})
+                       ctx.Visitors.Unowned = append(ctx.Visitors.Unowned, UnownedPropertyVisitor{visitorID: playerID, propertyID: propID})
                }
        case TypeRailroad:
-               propIndex := SpaceToOwnableProperty[nextPos]
-               if PropertyOwners[propIndex] != -1 { // property owned?
-                       ownerID := PropertyOwners[propIndex]
-                       if ownerID != playerID && !IsMortgaged(propIndex) { // not by you
-                               OwnedRailroadVisitors = append(OwnedRailroadVisitors, OwnedRailroadVisitor{visitorID: playerID, ownerID: ownerID, railroadID: SpaceToRespProperty[nextPos]})
+               propID := ctx.getPropID(nextPos)
+               ownerID := ctx.Properties.Owners[propID.Index()].OwnerID
+               if ownerID != BankPlayerID { // property owned?
+                       if ownerID != playerID && !ctx.IsMortgaged(propID) { // not by you
+                               ctx.Visitors.Railroad = append(ctx.Visitors.Railroad, OwnedRailroadVisitor{visitorID: playerID, ownerID: ownerID, railroadID: prop.SubIndexID})
                        }
                } else {
-                       UnownedPropertyVisitors = append(UnownedPropertyVisitors, UnownedPropertyVisitor{visitorID: playerID, propertyID: propIndex})
+                       ctx.Visitors.Unowned = append(ctx.Visitors.Unowned, UnownedPropertyVisitor{visitorID: playerID, propertyID: propID})
                }
        case TypeUtility:
-               propIndex := SpaceToOwnableProperty[nextPos]
-               if PropertyOwners[propIndex] != -1 { // property owned?
-                       ownerID := PropertyOwners[propIndex]
-                       if ownerID != playerID && !IsMortgaged(propIndex) { // not by you
-                               OwnedUtilityVisitors = append(OwnedUtilityVisitors, OwnedUtilityVisitor{visitorID: playerID, ownerID: ownerID, utilityID: SpaceToRespProperty[nextPos], diceRoll: diceRoll})
+               propID := ctx.getPropID(nextPos)
+               ownerID := ctx.Properties.Owners[propID.Index()].OwnerID
+               if ownerID != BankPlayerID { // property owned?
+                       if ownerID != playerID && !ctx.IsMortgaged(propID) { // not by you
+                               ctx.Visitors.Utility = append(ctx.Visitors.Utility, OwnedUtilityVisitor{visitorID: playerID, ownerID: ownerID, utilityID: prop.SubIndexID, diceRoll: diceRoll})
                        }
                } else {
-                       UnownedPropertyVisitors = append(UnownedPropertyVisitors, UnownedPropertyVisitor{visitorID: playerID, propertyID: propIndex})
+                       ctx.Visitors.Unowned = append(ctx.Visitors.Unowned, UnownedPropertyVisitor{visitorID: playerID, propertyID: propID})
                }
        }
 }
index ba79a570809ae7e10bfbdd547b4db84569bd5414..7ecab91fc5de99fa8caac0c488f7b352e41b0e6f 100644 (file)
@@ -1,27 +1,41 @@
 package game
 
-func numRailroadOwned(playerID int32) int32 {
+func (ctx *Context) numRailroadOwned(playerID PlayerID) int32 {
        var ownedCount int32 = 0
-       for propID, ownerID := range PropertyOwners {
-               if ownerID == playerID && OwnablePropertyType[int32(propID)] == TypeRailroad {
-                       ownedCount++
+       for _, prop := range ctx.Properties.Owners {
+               ownerID := prop.OwnerID
+
+               if ownerID != playerID {
+                       continue
+               }
+
+               spaceID := prop.SpaceID
+               space := BoardSpaces[spaceID.Index()]
+
+               propType := space.PropertyType
+               if propType != TypeRailroad {
+                       continue
                }
+
+               ownedCount++
+
        }
 
        return ownedCount
 }
 
-func ProcessOwnedRailroad() {
-       for _, oRV := range OwnedRailroadVisitors {
+func (ctx *Context) ProcessOwnedRailroad() {
+       for _, oRV := range ctx.Visitors.Railroad {
                visitorID := oRV.visitorID
                ownerID := oRV.ownerID
                // railroadID := oRV.railroadID
 
-               var rent int32 = RailroadRent[numRailroadOwned(ownerID)]
+               var rent int32 = RailroadRent[ctx.numRailroadOwned(ownerID)]
+
+               ctx.AdjustPlayerMoney(visitorID, -rent)
+               ctx.AdjustPlayerMoney(ownerID, rent)
 
-               AdjustPlayerMoney(visitorID, -rent)
-               AdjustPlayerMoney(ownerID, rent)
                // reset railroad rent mod after payment
-               ModifierRailroadRentMultiplier = 1
+               ctx.Turn.Modifier.RailroadRentMultiplier = 1
        }
 }
index 3c5213a5c98f37d58391403f3a85b9891af81837..414d3f75baa835e63f8cc5f6b10e17b6094394d8 100644 (file)
@@ -1,8 +1,9 @@
 package game
 
-func ProcessTax() {
-       for _, playerID := range TaxVisitors {
-               taxID := SpaceToTaxSpace[Users[playerID].CurrentSpaceID]
-               AdjustPlayerMoney(playerID, -TaxSpaces[taxID].Amount)
+func (ctx *Context) ProcessTax() {
+       for _, tV := range ctx.Visitors.Tax {
+               playerID := tV.visitorID
+               taxID := tV.taxID
+               ctx.AdjustPlayerMoney(playerID, -TaxSpaces[taxID].Amount)
        }
 }
diff --git a/game/todo b/game/todo
new file mode 100644 (file)
index 0000000..76c8585
--- /dev/null
+++ b/game/todo
@@ -0,0 +1,15 @@
+- TODO Trading
+        - validate no race condition
+        - prevent trading properties with buildings
+        - trade money
+        - negotiate / accept / decline / delete
+
+- TODO auction
+        - own auction
+        - bank liquidation auction
+
+- TODO Bankruptcy 
+
+- TODO user auth
+
+- TODO Server Side Events (SSE) for updating game on client
index a4fc9e9006873bef929b8fdd2a10d07e0a85f20b..5ad649c6d2c7d8f46dfdcafb7893980bf7e56866 100644 (file)
 package game
 
-type User struct {
+import (
+       "math/rand/v2"
+)
+
+type Player struct {
        UUID              string
        Money             int32
-       CurrentSpaceID    int32
+       CurrentSpaceID    SpaceID
        GetOutOfJailCards int32
+       CanMove           bool
 }
 
 type PropertyType int
+type Space struct {
+       PropertyType PropertyType
+       SubIndexID   int32 // FK to resp. OwnableProperty types
+}
 
-const (
-       TypeGo PropertyType = iota
-       TypeColor
-       TypeChest
-       TypeTax
-       TypeRailroad
-       TypeChance
-       TypeJail
-       TypeUtility
-       TypeParking
-       TypePolice
-)
+type PropertyStatic struct {
+       Name  string
+       Price int32
+}
+
+type OwnableProperty struct {
+       OwnerID PlayerID
+       SpaceID SpaceID
+}
+
+type ColorGroup int32
+
+type ColorProperty struct {
+       Name    string
+       Price   int32
+       GroupID ColorGroup
+       Houses  int32
+}
+
+type TaxSpace struct {
+       Name   string
+       Amount int32
+}
+
+type ChanceSpaceIDs struct {
+       StCharlesPlace  SpaceID
+       Go              SpaceID
+       ReadingRailroad SpaceID
+       Boardwalk       SpaceID
+       IllinoisAvenue  SpaceID
+}
+
+type OwnedColorVisitor struct {
+       visitorID PlayerID
+       ownerID   PlayerID
+       colorID   int32
+}
+
+type OwnedRailroadVisitor struct {
+       visitorID  PlayerID
+       ownerID    PlayerID
+       railroadID int32
+}
+
+type OwnedUtilityVisitor struct {
+       visitorID PlayerID
+       ownerID   PlayerID
+       utilityID int32
+       diceRoll  int32
+}
+
+type UnownedPropertyVisitor struct {
+       visitorID  PlayerID
+       propertyID PropertyID
+}
+
+type InJailVisitor struct {
+       visitorID PlayerID
+       TurnsLeft int32
+}
+
+type TaxVisitor struct {
+       visitorID PlayerID
+       taxID     int32
+}
+
+type Visitors struct { // SubIndexID is the PK for each resp table
+       Unowned  []UnownedPropertyVisitor
+       Color    []OwnedColorVisitor
+       Railroad []OwnedRailroadVisitor
+       Utility  []OwnedUtilityVisitor
+       Go       []PlayerID
+       Tax      []TaxVisitor
+       Chance   []PlayerID
+       Chest    []PlayerID
+       InJail   []InJailVisitor
+       // Parking  []PlayerID
+       // Police   []PlayerID
+}
+
+// IDs
+
+type PlayerID struct {
+       id int32
+}
+
+type PropertyID struct {
+       id int32
+}
+
+type SpaceID struct {
+       id int32
+}
+
+func (p *PlayerID) Index() int {
+       return int(p.id)
+}
+
+func (p *PropertyID) Index() int {
+       return int(p.id)
+}
+
+func (s *SpaceID) Index() int {
+       return int(s.id)
+}
+
+func Add(a SpaceID, b int32) SpaceID {
+       return SpaceID{id: a.id + b}
+}
+
+// Game Context (Ctx)
+type Modifiers struct {
+       RailroadRentMultiplier     int32
+       UtilityForceRentMultiplier bool
+}
+
+type Players struct {
+       Alive []Player // PlayerID is PK
+}
+
+type Turn struct {
+       Current            PlayerID
+       Ended              bool
+       DiceRollsRemaining int32
+       NumDiceRolled      int32
+       RolledDoubles      bool
+       MoveQueue          []int32
+       InDebt             bool
+       Modifier           Modifiers
+}
+
+type Properties struct {
+       Owners    []OwnableProperty // PropertyID is PK
+       Mortgages []PropertyID
+}
+
+type Context struct {
+       Random     *rand.Rand
+       MoveQueue  []int32
+       Players    Players
+       Turn       Turn // state reset every turn end
+       Visitors   Visitors
+       Properties Properties
+}
diff --git a/game/unowned.go b/game/unowned.go
new file mode 100644 (file)
index 0000000..bc4d164
--- /dev/null
@@ -0,0 +1,29 @@
+package game
+
+func (ctx *Context) ProcessUnowned() {
+       for _, uV := range ctx.Visitors.Unowned {
+               visitorID := uV.visitorID
+               propID := uV.propertyID
+
+               prop := ctx.Properties.Owners[propID.Index()]
+
+               // TODO: trigger buy or auction
+
+               // spaceID := prop.SpaceID
+               // space := BoardSpaces[spaceID.Index()]
+               //
+               // var price int32 = 0
+               //
+               // switch space.PropertyType {
+               // case TypeColor:
+               //      price = ColorProperties[space.SubIndexID].Price
+               // case TypeUtility:
+               //      price = UtilityPrice
+               // case TypeRailroad:
+               //      price = RailroadPrice
+               // }
+               //
+               // ctx.AdjustPlayerMoney(visitorID, -price)
+
+       }
+}
index 924fcd9ad46c13c101e85c3100949276c7853184..054bd6b5210da4b4968a4b648b1609ddba8bc1f2 100644 (file)
@@ -1,18 +1,31 @@
 package game
 
-func numUtilities(playerID int32) int32 {
+func (ctx *Context) NumUtilities(playerID PlayerID) int32 {
        var ownedCount int32 = 0
-       for propID, ownerID := range PropertyOwners {
-               if ownerID == playerID && OwnablePropertyType[int32(propID)] == TypeUtility {
-                       ownedCount++
+       for _, prop := range ctx.Properties.Owners {
+               ownerID := prop.OwnerID
+
+               if ownerID != playerID {
+                       continue
+               }
+
+               spaceID := prop.SpaceID
+               space := BoardSpaces[spaceID.Index()]
+
+               propType := space.PropertyType
+               if propType != TypeUtility {
+                       continue
                }
+
+               ownedCount++
+
        }
 
        return ownedCount
 }
 
-func ProcessOwnedUtility() {
-       for _, oUV := range OwnedUtilityVisitors {
+func (ctx *Context) ProcessOwnedUtility() {
+       for _, oUV := range ctx.Visitors.Utility {
                visitorID := oUV.visitorID
                ownerID := oUV.ownerID
                // utilityID := oUV.utilityID
@@ -20,15 +33,15 @@ func ProcessOwnedUtility() {
 
                var rent int32 = 0
 
-               if !ModifierUtilityForceRentMultiplier {
-                       rent = UtilityRentMult[numUtilities(ownerID)] * diceRoll
+               if !ctx.Turn.Modifier.UtilityForceRentMultiplier {
+                       rent = UtilityRentMult[ctx.NumUtilities(ownerID)] * diceRoll
                } else {
                        rent = 10 * diceRoll
                }
 
-               AdjustPlayerMoney(visitorID, -rent)
-               AdjustPlayerMoney(ownerID, rent)
+               ctx.AdjustPlayerMoney(visitorID, -rent)
+               ctx.AdjustPlayerMoney(ownerID, rent)
 
-               ModifierUtilityForceRentMultiplier = false
+               ctx.Turn.Modifier.UtilityForceRentMultiplier = false
        }
 }
diff --git a/main.go b/main.go
index 01ad29d21d347dbb31c92db3ef1fb7a90641fbda..1c48c06274d5889c96c80bbbb9f377842a7c79a0 100644 (file)
--- a/main.go
+++ b/main.go
@@ -4,21 +4,36 @@ import (
        "fmt"
        "io"
        "log"
+       "math/rand/v2"
        "monopoly-web/game"
        "net/http"
 )
 
+type Room struct {
+       gameCtx *game.Context
+}
+
+func initRoom() Room {
+       randSeed := rand.NewPCG(20, 26)
+       players := []game.Player{
+               game.InitPlayer(),
+       }
+
+       return Room{
+               gameCtx: game.InitCtx(randSeed, players),
+       }
+}
+
 func main() {
        fmt.Println("monopoly-web backend")
 
-       game.Users = append(game.Users, game.User{UUID: "abc", Money: 100, CurrentSpaceID: 0, GetOutOfJailCards: 0})
-       fmt.Println(game.Users)
+       room := initRoom()
 
        // register routes
        http.HandleFunc("/health", healthHandler)
-       http.HandleFunc("/api/v1/roll", rollDiceHandler)
-       http.HandleFunc("POST /api/v1/turn", endTurnHandler)
-       http.HandleFunc("POST /api/v1/exit-jail", exitJailHandler)
+       http.HandleFunc("/api/v1/roll", room.rollDiceHandler)
+       http.HandleFunc("POST /api/v1/turn", room.endTurnHandler)
+       http.HandleFunc("POST /api/v1/exit-jail", room.exitJailHandler)
 
        // listen and serve
        log.Fatal(http.ListenAndServe(":8080", nil))
@@ -30,27 +45,27 @@ func healthHandler(w http.ResponseWriter, req *http.Request) {
 
 const UUID = "abc" // TODO: UUID in cookie
 
-func rollDiceHandler(w http.ResponseWriter, req *http.Request) {
-       if game.ValidateCanRoll(UUID) {
-               game.RollDice()
-               game.ProcessMovement()
+func (r *Room) rollDiceHandler(w http.ResponseWriter, req *http.Request) {
+       if r.gameCtx.ValidateCanRoll(UUID) {
+               r.gameCtx.RollDice()
+               r.gameCtx.ProcessMovement()
        }
 }
 
-func endTurnHandler(w http.ResponseWriter, req *http.Request) {
-       if game.ValidateCanEndTurn(UUID) {
-               game.EndTurn()
+func (r *Room) endTurnHandler(w http.ResponseWriter, req *http.Request) {
+       if r.gameCtx.ValidateCanEndTurn(UUID) {
+               r.gameCtx.EndTurn()
        }
 }
 
-func exitJailHandler(w http.ResponseWriter, req *http.Request) {
+func (r *Room) exitJailHandler(w http.ResponseWriter, req *http.Request) {
        err := req.ParseForm()
        if err != nil {
                http.Error(w, "Bad Request: Failed to parse form data", http.StatusBadRequest)
                return
        }
 
-       if !game.ValidateCanExitJail(UUID) {
+       if !r.gameCtx.ValidateCanExitJail(UUID) {
                w.WriteHeader(http.StatusForbidden)
                w.Write([]byte(`{"status": "forbidden", "message": "Not your turn or not in jail"}`))
        }
@@ -59,13 +74,13 @@ func exitJailHandler(w http.ResponseWriter, req *http.Request) {
 
        switch method {
        case "buyout":
-               err = game.JailBuyout()
+               err = r.gameCtx.JailBuyout()
                if err == game.ErrNotEnoughMoney {
                        http.Error(w, "error: Insufficient funds", http.StatusUnprocessableEntity)
                }
 
        case "jail_free_card":
-               err = game.JailUseCard()
+               err = r.gameCtx.JailUseCard()
                if err == game.ErrNotEnoughJailCards {
                        http.Error(w, "error: Insufficient jail cards", http.StatusUnprocessableEntity)
                }