package game import ( "errors" "fmt" "io" "log" "math/rand" "net/http" "os" "path" "strings" "time" "gopkg.in/yaml.v3" ) const ( PROTOCOL string = "http://" BASE_URL string = "localhost:8000/" MAP_URL_PART string = "html/build/maps" DEFAULT_RESSOURCE_GAIN int = 5 DEFAULT_START_DECK string = "3 misc/farmer" ) type MapYml struct { Map string `yaml:"map"` Symbols map[string]string `yaml:"symbols"` } type Map struct { Tiles [][]Tile symbols map[string]string RessourceGain int StartDeckList string } func readMap(r io.Reader) (*Map, error) { data, err := io.ReadAll(r) if err != nil { return nil, err } return ParseMap(data) } func ParseMap(data []byte) (*Map, error) { mapYml := MapYml{} err := yaml.Unmarshal(data, &mapYml) if err != nil { return nil, err } mapYml.Symbols[" "] = "neutral" m := &Map{[][]Tile{}, mapYml.Symbols, DEFAULT_RESSOURCE_GAIN, DEFAULT_START_DECK} rows := strings.Split(mapYml.Map, "\n") for y := 0; y < len(rows); y++ { m.Tiles = append(m.Tiles, []Tile{}) row := rows[y] for x := 0; x < len(rows[y]); x++ { s := string(row[x]) tile, err := NewTileFromString(mapYml.Symbols[s], Position{x, y}) if err != nil { return nil, err } m.Tiles[y] = append(m.Tiles[y], tile) } } m.selectStreets() return m, nil } func (m *Map) selectStreets() { for y := 0; y < len(m.Tiles); y++ { for x := 0; x < len(m.Tiles[y]); x++ { tile := &m.Tiles[y][x] if tile.Type != TileTypes.Street { continue } connections, left, right, above, below := m.FindStreetConnections(x, y) // Decide if curve or straight if connections == 2 && (left || right) && (above || below) { tile.Raw = "street 2" } if connections > 2 { tile.Raw = fmt.Sprintf("street %d", connections) } } } } func (m *Map) FindNeighbours(x, y int) (left, right, above, below *Tile) { left, right, above, below = nil, nil, nil, nil if y > 0 { above = &m.Tiles[y-1][x] } if y < len(m.Tiles) { below = &m.Tiles[y+1][x] } if x > 0 { left = &m.Tiles[y][x-1] } if x < len(m.Tiles[y]) { right = &m.Tiles[y][x+1] } return left, right, above, below } func (m *Map) FindAnyConnections(x, y int) (connections int, left, right, above, below bool) { leftT, rightT, aboveT, belowT := m.FindNeighbours(x, y) connections = 0 if leftT != nil && leftT.Type != TileTypes.Neutral { left = true connections += 1 } if rightT != nil && rightT.Type != TileTypes.Neutral { right = true connections += 1 } if aboveT != nil && aboveT.Type != TileTypes.Neutral { above = true connections += 1 } if belowT != nil && belowT.Type != TileTypes.Neutral { below = true connections += 1 } return } func (m *Map) FindStreetConnections(x int, y int) (connections int, left, right, above, below bool) { leftT, rightT, aboveT, belowT := m.FindNeighbours(x, y) connections = 0 if leftT != nil && leftT.Type == TileTypes.Street { left = true connections += 1 } if rightT != nil && rightT.Type == TileTypes.Street { right = true connections += 1 } if aboveT != nil && aboveT.Type == TileTypes.Street { above = true connections += 1 } if belowT != nil && belowT.Type == TileTypes.Street { below = true connections += 1 } return } func GetMap(name string) (*Map, error) { url := path.Join(BASE_URL, MAP_URL_PART, name+".yml") log.Printf("loading map from %s\n", url) resp, err := http.Get(PROTOCOL + url) if err != nil { log.Fatal(err) } defer resp.Body.Close() return readMap(resp.Body) } func LoadMap(file string) (*Map, error) { f, err := os.Open(file) if err != nil { log.Fatal("Opening map ", file, " failed: ", err) } defer f.Close() return readMap(f) } func (m *Map) TileAt(p Position) *Tile { return &m.Tiles[p.Y][p.X] } func (m *Map) RandomTile(types []TileType) Position { candidates := []Position{} for y := 0; y < len(m.Tiles); y++ { for x := 0; x < len(m.Tiles[y]); x++ { candidate := m.Tiles[y][x] for _, t := range types { if candidate.Type == t { candidates = append(candidates, candidate.Position) break } } } } r := rand.New(rand.NewSource(time.Now().UnixNano())) randIdx := r.Intn(len(candidates)) return candidates[randIdx] } func (m *Map) RandomTileFromSymbols(symbols []string) (Position, error) { types := make([]TileType, 0, len(symbols)) for _, symbol := range symbols { tileString, ok := m.symbols[symbol] if !ok { return INVALID_POSITION(), fmt.Errorf(fmt.Sprintf("symbol %s not in the map symbols %v", symbol, m.symbols)) } tileType, ok := TileNames[tileString] if !ok { return INVALID_POSITION(), errors.New("tile type unknown") } types = append(types, tileType) } return m.RandomTile(types), nil }