-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
148 lines (119 loc) · 3.82 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"log"
"os"
"strings"
"github.com/alexanderbez/alien-invasion/simulation"
"github.com/alexanderbez/alien-invasion/world"
)
func main() {
var (
mapFile string
outFile string
numAliens uint
)
flag.StringVar(&mapFile, "map", "", "file containing the map definition")
flag.StringVar(&outFile, "out", "", "output file to write resulting map to")
flag.UintVar(&numAliens, "n", 0, "number of aliens to use in the simulation")
flag.Parse()
if len(mapFile) == 0 {
cmdErrorMsg("invalid map definition: no file specified")
} else if len(outFile) == 0 {
cmdErrorMsg("invalid output definition: no file specified")
} else if numAliens == 0 {
cmdErrorMsg("invalid number of aliens: must be greater than zero")
}
worldMap, err := buildWorldMap(mapFile)
if err != nil {
log.Fatalf("failed to build map from file: %v", err)
}
// We assume there can be no more than twice the number of aliens as there
// are cities. In otherwords, upon seeding the map with aliens, at most each
// can be occupied by two aliens.
if numAliens > worldMap.NumCities()*2 {
log.Fatalf("invalid number of aliens: cannot have more than 2x of unique cities")
}
// Seed the map with 'n' aliens scattered randomly throughout the city and
// invoke an initial series of alien fights where a search of the map
// (graph) is done looking for city alien occupation equal to MaxOccupancy.
worldMap.SeedAliens(numAliens)
worldMap.ExecuteFights()
sim := simulation.NewSimulation(worldMap)
if err := sim.Run(); err != nil {
log.Fatalf("failed to execute alien invasion simulation: %v", err)
}
log.Println("simulation complete")
if err := writeMapToFile(worldMap, outFile); err != nil {
log.Fatalf("failed to write map to file: %v", err)
}
}
func cmdErrorMsg(errMsg string) {
fmt.Printf("%s\n\n", errMsg)
fmt.Println("usage:")
flag.PrintDefaults()
os.Exit(1)
}
// buildWorldMap builds a map from a give file. The map definition file has one
// city per line. The city name is first, followed by 1-4 directions
// (north, south, east, or west). Each one represents a road to another city
// that lies in that direction. The city and each of the pairs are separated by
// a single space, and the directions are separated from their respective
// cities with an equals (=) sign. An error is returned if reading the file
// fails at any point or if the map definition does not adhere to the given
// schema.
func buildWorldMap(mapFile string) (*world.Map, error) {
file, err := os.Open(mapFile)
if err != nil {
return nil, err
}
defer file.Close()
worldMap := world.NewMap()
// Create a scanner to read each map entry line by line
//
// Note: We assume the line entry can fit into the scanner's buffer
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
tokens := strings.Split(line, " ")
if len(tokens) == 0 {
return nil, errors.New("invalid line in map definition")
}
cityName := tokens[0]
if len(tokens) > 1 {
for _, link := range tokens[1:] {
linkTokens := strings.Split(link, "=")
if len(linkTokens) != 2 {
return nil, errors.New("invalid line in map definition")
}
worldMap.AddLink(cityName, linkTokens[0], linkTokens[1])
}
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
return worldMap, nil
}
// writeMapToFile writes a given world map to the file at path 'outPath'. An
// error is returned if the file cannot be created or written to.
func writeMapToFile(worldMap *world.Map, outPath string) error {
fileHandle, err := os.Create(outPath)
if err != nil {
return err
}
defer fileHandle.Close()
writer := bufio.NewWriter(fileHandle)
defer fileHandle.Close()
for _, city := range worldMap.Cities() {
s := city.String()
if len(s) != 0 {
fmt.Fprintln(writer, s)
writer.Flush()
}
}
return nil
}