├── .github └── workflows │ ├── manual.yml-1 │ └── nightly.yml-1 ├── .gitignore ├── FAQ.md ├── LICENSE ├── README.md ├── build.sh ├── builtin ├── COPYRIGHT ├── LICENSE ├── builtin.go ├── maths.go ├── misc.go └── string.go ├── cpu.go ├── delay ├── ddelay.go ├── udelay.go └── wdelay.go ├── dev-docs └── basicbots-dev.md ├── display.go ├── docs ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── EFFECTIVE_GO.md ├── ROADMAP.md ├── SECURITY.md ├── STYLE_GUIDE.md ├── STYLE_GUIDE_1.md ├── STYLE_GUIDE_2.md ├── UPDATES.md └── truthtable.ods ├── documentation ├── BASIC-old.md ├── BASIC.md ├── BASICBOTS.md ├── CODE-EXAMPLES.md └── LICENSE ├── endgame.go ├── eval ├── COPYRIGHT ├── LICENSE ├── eval.go ├── for_loop.go ├── stack.go └── vars.go ├── functions.go ├── globals.go ├── go.mod ├── go.sum ├── images ├── basicbots-logo-512x512.png ├── example1.gif ├── example2.gif ├── example4.gif ├── example5.gif └── example6.gif ├── latest └── txtfile ├── main.go ├── makefile-nightly ├── makefile-no ├── makewindows.bat-no ├── missile.go ├── movement.go ├── object ├── COPYRIGHT ├── LICENSE └── object.go ├── robots.go ├── robots ├── blaster.bas ├── corner-runner.bas ├── corner-v2.bas ├── nexus.bas ├── rabbit-v1.bas ├── rook.bas ├── sniper-v1.bas └── target.bas ├── scan.go ├── testbots ├── bench.bas ├── bench2.bas ├── bench3.bas └── shooter.bas ├── token ├── COPYRIGHT ├── LICENSE └── token.go ├── tokenizer ├── COPYRIGHT ├── LICENSE └── tokenizer.go └── workspace.code-workspace /.github/workflows/manual.yml-1: -------------------------------------------------------------------------------- 1 | name: Manual Build 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Install dependencies 14 | run: make 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml-1: -------------------------------------------------------------------------------- 1 | name: Nightly Build 2 | 3 | on: 4 | schedule: 5 | # * is a special character in YAML so you have to quote this string 6 | - cron: '19 7 * * *' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install dependencies 17 | run: make 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Go 2 | # Binaries for programs and plugins 3 | bin/* 4 | !bin/README.md 5 | *.exe 6 | *.dll 7 | *.so 8 | *.dylib 9 | test.bat 10 | 11 | # I dont think binaries should be in the branches. 12 | linux64.tgz 13 | windows64.zip 14 | basicbots 15 | latest 16 | tournament/tournament 17 | 18 | 19 | # Test binary, built with `go test -c` 20 | *.test 21 | 22 | # go files 23 | # go.mod 24 | # go.sum 25 | 26 | # Output of the go coverage tool, specifically when used with LiteIDE 27 | *.out 28 | 29 | # Dependency directories (remove the comment below to include it) 30 | # vendor/ 31 | l.txt 32 | ll.txt 33 | notes.txt 34 | 35 | # ignore IDEA configuration 36 | *.iml 37 | .idea/ 38 | 39 | # ignore temp files generated by other editors 40 | *~ 41 | 42 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | Q. Does basicbots support other languages besides basic? 4 | A. No. Basic is built into the program. This makes sure each program recieves the same amount of time per cycle. 5 | 6 | Q. Are you going to i,pliment other languages? 7 | A. No. See above. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **BasicBots**🤖 2 | 3 | # July 15 2024 4 | BasicBots development is continuing. Join me! 5 | 6 | ## **What is BasicBots?** 7 | **BasicBots** is a game that gives you the experience of a programming a robot tank to fight other programs in an arena. 8 | 9 | ## **My Inspiration** 10 | My first exposure to programming combat games was [Tom Poindexter's](https://github.com/tpoindex) original [crobots](http://tpoindex.github.io/crobots/) from 1985, for me it is still a pleasure to play. 11 | 12 | ## **User Interface** 13 | I didn't want to lose the look, feel and charm of the original insperation so it has the same ASCII effect that creates an amazing view for the player. 14 | 15 | ## **Programming Language** 16 | **BasicBots** uses a subset of *BASIC*. Why *BASIC*? Because, honestly, that's what I wanted. To give the feel of programming on the old 8 bit machines of the late 70s and early 80s. Besides, its fun. 17 | 18 | ## [FAQ](FAQ.md) 19 | 20 | ## **Build Instruction** 21 | Things are changing. Check back. 22 | 23 | ## **My First Open Source Contribution** 24 | 25 | I expect it will take time for me to learn the do's and dont's of managing an Open Source project. 26 | The project is open for all the contributors to engage and make an impact, improving the game. 27 | 28 | - Fixing spelling errors and typos. 29 | - Creating documentation. 30 | - Documenting the code. 31 | - Squashing bugs. 32 | - Adding features. 33 | - Suggesting enhancements. 34 | - Guiding me to a better understanding of Open Source projects. 35 | 36 | --- 37 | 38 | - [basicbots guide](https://github.com/misterunix/basicbots/blob/main/documentation/BASICBOTS.md) 39 | 40 | --- 41 | 42 | 43 |

44 | 45 |

46 | 47 | --- 48 | 49 | ## Teams with simple robots. 50 | 51 |

52 | 53 |

54 | 55 | ## Match with teams 56 | ``` 57 | ./basicbots -t -m 111 testbots/shooter.bas testbots/teamtest.bas testbots/shooter.bas testbots/teamtest.bas 58 | shooter.bas w:00052 t:00002 l:00057 p:00156 59 | teamtest.bas w:00052 t:00001 l:00057 p:00131 60 | shooter.bas w:00057 t:00001 l:00052 p:00172 61 | teamtest.bas w:00057 t:00002 l:00052 p:00143 62 | Team1 w:00104 t:00003 l:00114 p:00287 63 | Team2 w:00114 t:00003 l:00104 p:00315 64 | ``` 65 | 66 | ## Updates 67 | - Nightly builds will be put on hold until I can find a way to automate it. 68 | - Teams is working. 69 | - Demo and docs coming soon. 70 | 71 | ## Contributors 72 | - [People who deserve recognition.](docs/CONTRIBUTORS.md) 73 | 74 | ## Guidlines 75 | - [Contributing](docs/CONTRIBUTING.md) 76 | - [Updates](docs/UPDATES.md) 77 | 78 | **connect with me on** 79 | 80 |
81 | Twitter: 82 | 83 | @misterunix 84 | 85 | and @basicbots 86 |
87 | 📧 email address: misterunix@gmail.com 88 | Reach out with any questions. 89 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | # Build the project 5 | echo "cleaning bin directory" 6 | rm -rf bin/* 7 | 8 | echo "compiling linux amd64" 9 | GOOS=linux GOARCH=amd64 go build -o bin/basicbots-linux_amd64 10 | 11 | echo "compiling linux arm64" 12 | GOOS=linux GOARCH=arm64 go build -o bin/basicbots-linux_arm64 13 | 14 | echo "compiling windows amd64" 15 | GOOS=windows GOARCH=amd64 go build -o bin/basicbots-windows_amd64.exe 16 | 17 | echo "compiling darwin amd64" 18 | GOOS=darwin GOARCH=amd64 go build -o bin/basicbots-darwin_amd64 19 | 20 | echo "compiling darwin arm64" 21 | GOOS=darwin GOARCH=arm64 go build -o bin/basicbots-darwin_arm64 22 | 23 | # this is where I have my basicbots binaries 24 | # you can change this to your own path and machine type 25 | # cp bin/basicbots-linux_amd64 ~/basicbots/bin/ 26 | -------------------------------------------------------------------------------- /builtin/COPYRIGHT: -------------------------------------------------------------------------------- 1 | gobasic is Copyrighted by Steve Kemp. 2 | Licensed under gpl-v2. Used by permission. 3 | You can find the original 'gobasic' at https://github.com/skx/gobasic 4 | -------------------------------------------------------------------------------- /builtin/builtin.go: -------------------------------------------------------------------------------- 1 | // Package builtin contains the implementation for the core functions 2 | // implemented for BASIC, such as SIN, COS, RND, PRINT, etc. 3 | // 4 | // The builtin package also provides an interface which with you can 5 | // extent the interpreter to supply new primitives, for example you 6 | // might implement RANDOM, PEEK, POKE, or something entirely different. 7 | // 8 | // Examples of extending the interpreter exist within the `goserver/` 9 | // and `embed/` packages within the distribution. 10 | // 11 | //gobasic is Copyrighted by Steve Kemp. 12 | //Licensed under gpl-v2. Used by permission. 13 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 14 | package builtin 15 | 16 | import ( 17 | "basicbots/object" 18 | "bufio" 19 | "sync" 20 | ) 21 | 22 | // Signature is the signature of a builtin-function. 23 | // 24 | // Each built-in will receive an array of objects, and will return a 25 | // single object back to the caller. 26 | // 27 | // In the case of an error then the object will be an error-object. 28 | type Signature func(env Environment, args []object.Object) object.Object 29 | 30 | // Builtins holds our state. 31 | type Builtins struct { 32 | // lock holds a mutex to prevent corruption. 33 | lock sync.Mutex 34 | 35 | // argRegistry holds the number of arguments the given name requires. 36 | argRegistry map[string]int 37 | 38 | // fnRegistry holds a reference to the golang function which 39 | // implements the builtin. 40 | fnRegistry map[string]Signature 41 | } 42 | 43 | // New returns a new helper/holder for builtin functions. 44 | func New() *Builtins { 45 | t := &Builtins{} 46 | t.argRegistry = make(map[string]int) 47 | t.fnRegistry = make(map[string]Signature) 48 | 49 | return t 50 | } 51 | 52 | // Register records a built-in function. 53 | // The three arguments are: 54 | // NAME - The thing that the BASIC program will call 55 | // nARGS - The number of arguments the built-in requires. 56 | // NOTE: Arguments are comma-separated in the BASIC program, 57 | // but commas are stripped out. 58 | // FT - The function which provides the implementation. 59 | func (b *Builtins) Register(name string, nArgs int, ft Signature) { 60 | b.lock.Lock() 61 | defer b.lock.Unlock() 62 | 63 | // Record the details. 64 | b.argRegistry[name] = nArgs 65 | b.fnRegistry[name] = ft 66 | } 67 | 68 | // Get the values associated with the given built-in. 69 | func (b *Builtins) Get(name string) (int, Signature) { 70 | b.lock.Lock() 71 | defer b.lock.Unlock() 72 | 73 | return b.argRegistry[name], b.fnRegistry[name] 74 | } 75 | 76 | // Environment is an interface which is passed to all built-in functions. 77 | type Environment interface { 78 | // StdInput is a handle to a reader-object, allowing input to 79 | // be processed by the built-ins. 80 | StdInput() *bufio.Reader 81 | 82 | // StdOutput is a handle to a writer-object, allowing output to 83 | // be generated by the built-ins. 84 | StdOutput() *bufio.Writer 85 | 86 | // StdError is a handle to a writer-object, allowing user errors to 87 | // be communicated by the built-ins. 88 | StdError() *bufio.Writer 89 | 90 | // LineEnding specifies any additional characters that should be 91 | // appended to PRINT commands - for example '\n' 92 | LineEnding() string 93 | 94 | // Data allows the builtins to get a reference to the intepreter. 95 | Data() interface{} 96 | } 97 | -------------------------------------------------------------------------------- /builtin/maths.go: -------------------------------------------------------------------------------- 1 | // The builtin package provides the ability to register our built-in functions. 2 | // 3 | // maths.go implements our math-related primitives 4 | // All trig funcitons are passed and returned as degrees 5 | //gobasic is Copyrighted by Steve Kemp. 6 | //Licensed under gpl-v2. Used by permission. 7 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 8 | 9 | package builtin 10 | 11 | import ( 12 | "fmt" 13 | "math" 14 | "math/rand" 15 | "strconv" 16 | "time" 17 | 18 | "basicbots/object" 19 | ) 20 | 21 | // For math 22 | const ( 23 | DEG2RAD = 0.0174532925 24 | RAD2DEG = 57.2957795130 25 | ) 26 | 27 | // init ensures that we've initialized our random-number state 28 | func init() { 29 | rand.Seed(time.Now().UnixNano()) 30 | } 31 | 32 | // ABS implements ABS 33 | func ABS(env Environment, args []object.Object) object.Object { 34 | 35 | // Get the (float) argument. 36 | if args[0].Type() != object.NUMBER { 37 | return object.Error("Wrong type") 38 | } 39 | i := args[0].(*object.NumberObject).Value 40 | 41 | // If less than zero make it positive. 42 | if i < 0 { 43 | return &object.NumberObject{Value: -1 * i} 44 | } 45 | 46 | // Otherwise return as-is. 47 | return &object.NumberObject{Value: i} 48 | } 49 | 50 | // ACS (arccosine) 51 | func ACS(env Environment, args []object.Object) object.Object { 52 | 53 | // Get the (float) argument. 54 | if args[0].Type() != object.NUMBER { 55 | return object.Error("Wrong type") 56 | } 57 | i := args[0].(*object.NumberObject).Value 58 | r := math.Acos(i * DEG2RAD) 59 | r = math.Mod(r, 360) 60 | if r < 0.0 { 61 | r += 360.0 62 | } 63 | if r > 360.0 { 64 | r -= 360.0 65 | } 66 | return &object.NumberObject{Value: r} 67 | } 68 | 69 | // ASN (arcsine) 70 | func ASN(env Environment, args []object.Object) object.Object { 71 | 72 | // Get the (float) argument. 73 | if args[0].Type() != object.NUMBER { 74 | return object.Error("Wrong type") 75 | } 76 | i := args[0].(*object.NumberObject).Value 77 | r := math.Asin(i * DEG2RAD) 78 | r = math.Mod(r, 360) 79 | if r < 0.0 { 80 | r += 360.0 81 | } 82 | if r > 360.0 { 83 | r -= 360.0 84 | } 85 | return &object.NumberObject{Value: r} 86 | } 87 | 88 | // ATN (arctan) 89 | func ATN(env Environment, args []object.Object) object.Object { 90 | 91 | // Get the (float) argument. 92 | if args[0].Type() != object.NUMBER { 93 | return object.Error("Wrong type") 94 | } 95 | i := args[0].(*object.NumberObject).Value 96 | r := math.Atan(i * DEG2RAD) 97 | r = math.Mod(r, 360) 98 | if r < 0.0 { 99 | r += 360.0 100 | } 101 | if r > 360.0 { 102 | r -= 360.0 103 | } 104 | return &object.NumberObject{Value: r} 105 | } 106 | 107 | // BIN converts a number from binary. 108 | func BIN(env Environment, args []object.Object) object.Object { 109 | 110 | // Get the (float) argument. 111 | if args[0].Type() != object.NUMBER { 112 | return object.Error("Wrong type") 113 | } 114 | i := args[0].(*object.NumberObject).Value 115 | 116 | s := fmt.Sprintf("%d", int(i)) 117 | 118 | b, err := strconv.ParseInt(s, 2, 64) 119 | if err != nil { 120 | return object.Error("BIN:%s", err.Error()) 121 | } 122 | 123 | return &object.NumberObject{Value: float64(b)} 124 | 125 | } 126 | 127 | // COS implements the COS function.. 128 | func COS(env Environment, args []object.Object) object.Object { 129 | 130 | // Get the (float) argument. 131 | if args[0].Type() != object.NUMBER { 132 | return object.Error("Wrong type") 133 | } 134 | i := args[0].(*object.NumberObject).Value 135 | i = math.Mod(i, 360) 136 | if i < 0.0 { 137 | i += 360.0 138 | } 139 | if i > 360.0 { 140 | i -= 360.0 141 | } 142 | r := math.Cos(i * DEG2RAD) 143 | 144 | return &object.NumberObject{Value: r} 145 | } 146 | 147 | // EXP x=e^x EXP 148 | func EXP(env Environment, args []object.Object) object.Object { 149 | // Get the (float) argument. 150 | if args[0].Type() != object.NUMBER { 151 | return object.Error("Wrong type") 152 | } 153 | i := args[0].(*object.NumberObject).Value 154 | 155 | return &object.NumberObject{Value: math.Exp(i)} 156 | } 157 | 158 | // INT implements INT 159 | func INT(env Environment, args []object.Object) object.Object { 160 | 161 | // Get the (float) argument. 162 | if args[0].Type() != object.NUMBER { 163 | return object.Error("Wrong type") 164 | } 165 | i := args[0].(*object.NumberObject).Value 166 | 167 | // Truncate. 168 | return &object.NumberObject{Value: float64(int(i))} 169 | } 170 | 171 | // LN calculates logarithms to the base e - LN 172 | func LN(env Environment, args []object.Object) object.Object { 173 | 174 | // Get the (float) argument. 175 | if args[0].Type() != object.NUMBER { 176 | return object.Error("Wrong type") 177 | } 178 | i := args[0].(*object.NumberObject).Value 179 | 180 | return &object.NumberObject{Value: math.Log(i)} 181 | } 182 | 183 | // PI returns the value of PI 184 | func PI(env Environment, args []object.Object) object.Object { 185 | return &object.NumberObject{Value: math.Pi} 186 | } 187 | 188 | // RND implements RND 189 | func RND(env Environment, args []object.Object) object.Object { 190 | 191 | // Get the (float) argument. 192 | if args[0].Type() != object.NUMBER { 193 | return object.Error("Wrong type") 194 | } 195 | i := args[0].(*object.NumberObject).Value 196 | 197 | // convert to int 198 | n := int(i) 199 | 200 | // Ensure it is valid. 201 | if n < 1 { 202 | return object.Error("Argument to RND must be >0") 203 | } 204 | 205 | // Return the random number 206 | return &object.NumberObject{Value: float64(rand.Intn(n))} 207 | } 208 | 209 | // SGN is the sign function (sometimes called signum). 210 | func SGN(env Environment, args []object.Object) object.Object { 211 | 212 | // Get the (float) argument. 213 | if args[0].Type() != object.NUMBER { 214 | return object.Error("Wrong type") 215 | } 216 | i := args[0].(*object.NumberObject).Value 217 | 218 | if i < 0 { 219 | return &object.NumberObject{Value: -1} 220 | } 221 | if i == 0 { 222 | return &object.NumberObject{Value: 0} 223 | } 224 | return &object.NumberObject{Value: 1} 225 | 226 | } 227 | 228 | // SIN operats the sin function. 229 | func SIN(env Environment, args []object.Object) object.Object { 230 | 231 | // Get the (float) argument. 232 | if args[0].Type() != object.NUMBER { 233 | return object.Error("Wrong type") 234 | } 235 | i := args[0].(*object.NumberObject).Value 236 | i = math.Mod(i, 360) 237 | if i < 0.0 { 238 | i += 360.0 239 | } 240 | if i > 360.0 { 241 | i -= 360.0 242 | } 243 | 244 | r := math.Sin(i * DEG2RAD) 245 | return &object.NumberObject{Value: r} 246 | } 247 | 248 | // SQR implements square root. 249 | // Modified 2021 by Bill Jones for use in basicbots. 250 | func SQR(env Environment, args []object.Object) object.Object { 251 | 252 | // Get the (float) argument. 253 | if args[0].Type() != object.NUMBER { 254 | return object.Error("Wrong type") 255 | } 256 | i := args[0].(*object.NumberObject).Value 257 | 258 | // Ensure it is valid. 259 | if i < 1 { 260 | // return object.Error("Argument to SQR must be >0") 261 | return &object.NumberObject{Value: 0} 262 | } 263 | return &object.NumberObject{Value: math.Sqrt(i)} 264 | } 265 | 266 | // TAN implements the tan function. 267 | func TAN(env Environment, args []object.Object) object.Object { 268 | 269 | // Get the (float) argument. 270 | if args[0].Type() != object.NUMBER { 271 | return object.Error("Wrong type") 272 | } 273 | i := args[0].(*object.NumberObject).Value 274 | 275 | r := math.Tan(i * DEG2RAD) 276 | r = math.Mod(r, 360) 277 | if r < 0.0 { 278 | r += 360.0 279 | } 280 | if r > 360.0 { 281 | r -= 360.0 282 | } 283 | return &object.NumberObject{Value: r} 284 | } 285 | 286 | // ATN2 (arctan) 287 | // Added 2021 by Bill Jones for use in basicbots. 288 | func ATN2(env Environment, args []object.Object) object.Object { 289 | 290 | // Get the (x float) argument. 291 | if args[0].Type() != object.NUMBER { 292 | return object.Error("Wrong type") 293 | } 294 | y := args[0].(*object.NumberObject).Value 295 | 296 | // Get the (x float) argument. 297 | if args[1].Type() != object.NUMBER { 298 | return object.Error("Wrong type") 299 | } 300 | x := args[1].(*object.NumberObject).Value 301 | 302 | r := math.Atan2(y, x) * RAD2DEG 303 | r = math.Mod(r, 360.0) 304 | if r < 0.0 { 305 | r += 360.0 306 | } 307 | if r > 360.0 { 308 | r -= 360.0 309 | } 310 | 311 | return &object.NumberObject{Value: r} 312 | } 313 | -------------------------------------------------------------------------------- /builtin/misc.go: -------------------------------------------------------------------------------- 1 | // The builtin package provides the ability to register our built-in functions. 2 | // 3 | // misc.go implements some misc. primitives. 4 | //gobasic is Copyrighted by Steve Kemp. 5 | //Licensed under gpl-v2. Used by permission. 6 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 7 | 8 | package builtin 9 | 10 | import ( 11 | "bufio" 12 | "fmt" 13 | "os" 14 | 15 | "basicbots/object" 16 | ) 17 | 18 | // DUMP just displays the only argument it received. 19 | func DUMP(env Environment, args []object.Object) object.Object { 20 | var out *bufio.Writer 21 | if env == nil { 22 | out = bufio.NewWriter(os.Stdout) 23 | } else { 24 | out = env.StdOutput() 25 | } 26 | 27 | // Get the (float) argument. 28 | if args[0].Type() == object.NUMBER { 29 | i := args[0].(*object.NumberObject).Value 30 | out.WriteString(fmt.Sprintf("NUMBER: %f\n", i)) 31 | } 32 | if args[0].Type() == object.STRING { 33 | s := args[0].(*object.StringObject).Value 34 | out.WriteString(fmt.Sprintf("STRING: %s\n", s)) 35 | } 36 | if args[0].Type() == object.ERROR { 37 | s := args[0].(*object.ErrorObject).Value 38 | out.WriteString(fmt.Sprintf("Error: %s\n", s)) 39 | } 40 | out.Flush() 41 | 42 | // Otherwise return as-is. 43 | return &object.NumberObject{Value: 0} 44 | } 45 | 46 | // PRINT handles displaying strings, integers, and errors. 47 | func PRINT(env Environment, args []object.Object) object.Object { 48 | var out *bufio.Writer 49 | if env == nil { 50 | out = bufio.NewWriter(os.Stdout) 51 | } else { 52 | out = env.StdOutput() 53 | } 54 | for _, ent := range args { 55 | switch ent.Type() { 56 | case object.NUMBER: 57 | n := ent.(*object.NumberObject).Value 58 | if n == float64(int(n)) { 59 | out.WriteString(fmt.Sprintf("%d", int(n))) 60 | } else { 61 | out.WriteString(fmt.Sprintf("%f", n)) 62 | } 63 | case object.STRING: 64 | out.WriteString(ent.(*object.StringObject).Value) 65 | case object.ERROR: 66 | out.WriteString(ent.(*object.ErrorObject).Value) 67 | } 68 | } 69 | if env != nil { 70 | out.WriteString(env.LineEnding()) 71 | } 72 | out.Flush() 73 | 74 | // Return the count of values we printed. 75 | return &object.NumberObject{Value: float64(len(args))} 76 | } 77 | -------------------------------------------------------------------------------- /builtin/string.go: -------------------------------------------------------------------------------- 1 | // The builtin package provides the ability to register our built-in functions. 2 | // 3 | // string.go implements our string-related primitives 4 | //gobasic is Copyrighted by Steve Kemp. 5 | //Licensed under gpl-v2. Used by permission. 6 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 7 | 8 | package builtin 9 | 10 | import ( 11 | "fmt" 12 | "strconv" 13 | "unicode/utf8" 14 | 15 | "basicbots/object" 16 | ) 17 | 18 | // CHR returns the character specified by the given ASCII code. 19 | func CHR(env Environment, args []object.Object) object.Object { 20 | 21 | // Get the (float) argument. 22 | if args[0].Type() != object.NUMBER { 23 | return object.Error("Wrong type") 24 | } 25 | i := args[0].(*object.NumberObject).Value 26 | 27 | // ensure it is positive 28 | if i < 0 { 29 | return object.Error("Positive argument only") 30 | } 31 | 32 | // Now 33 | r := rune(i) 34 | 35 | return &object.StringObject{Value: string(r)} 36 | } 37 | 38 | // CODE returns the integer value of the specified character. 39 | func CODE(env Environment, args []object.Object) object.Object { 40 | 41 | // Get the (string) argument. 42 | if args[0].Type() != object.STRING { 43 | return object.Error("Wrong type") 44 | } 45 | 46 | // We convert this to an array of runes because we 47 | // want to handle unicode strings. 48 | i := []rune(args[0].(*object.StringObject).Value) 49 | 50 | if len(i) > 0 { 51 | s := rune(i[0]) 52 | return &object.NumberObject{Value: float64(rune(s))} 53 | } 54 | return &object.NumberObject{Value: float64(0)} 55 | 56 | } 57 | 58 | // LEFT returns the N left-most characters of the string. 59 | func LEFT(env Environment, args []object.Object) object.Object { 60 | 61 | // Get the (string) argument. 62 | if args[0].Type() != object.STRING { 63 | return object.Error("Wrong type") 64 | } 65 | 66 | // We convert this to an array of runes because we 67 | // want to handle unicode strings. 68 | in := []rune(args[0].(*object.StringObject).Value) 69 | 70 | // Get the (float) argument. 71 | if args[1].Type() != object.NUMBER { 72 | return object.Error("Wrong type") 73 | } 74 | n := int(args[1].(*object.NumberObject).Value) 75 | 76 | // ensure it is positive 77 | if n < 0 { 78 | return object.Error("Positive argument only") 79 | } 80 | 81 | if n > len(in) { 82 | n = len(in) 83 | } 84 | 85 | left := in[0:int(n)] 86 | 87 | return &object.StringObject{Value: string(left)} 88 | } 89 | 90 | // LEN returns the length of the given string 91 | func LEN(env Environment, args []object.Object) object.Object { 92 | 93 | // Get the (string) argument. 94 | if args[0].Type() != object.STRING { 95 | return object.Error("Wrong type") 96 | } 97 | in := args[0].(*object.StringObject).Value 98 | 99 | // We need to count in UTF-8 characters. 100 | len := utf8.RuneCountInString(in) 101 | 102 | return &object.NumberObject{Value: float64(len)} 103 | } 104 | 105 | // MID returns the N characters from the given offset 106 | func MID(env Environment, args []object.Object) object.Object { 107 | 108 | // Get the (string) argument. 109 | if args[0].Type() != object.STRING { 110 | return object.Error("Wrong type") 111 | } 112 | 113 | // We convert this to an array of runes because we 114 | // want to handle unicode strings. 115 | in := []rune(args[0].(*object.StringObject).Value) 116 | 117 | // Get the (float) argument. 118 | if args[1].Type() != object.NUMBER { 119 | return object.Error("Wrong type") 120 | } 121 | offset := int(args[1].(*object.NumberObject).Value) 122 | if offset < 0 { 123 | return object.Error("Positive argument only") 124 | } 125 | 126 | // Get the (float) argument. 127 | if args[2].Type() != object.NUMBER { 128 | return object.Error("Wrong type") 129 | } 130 | count := int(args[2].(*object.NumberObject).Value) 131 | if count < 0 { 132 | return object.Error("Positive argument only") 133 | } 134 | 135 | // too far 136 | if offset > len(in) { 137 | return &object.StringObject{Value: ""} 138 | } 139 | 140 | // get the string from the position 141 | out := in[offset:] 142 | 143 | // now cut, by length 144 | if count >= len(out) { 145 | count = len(out) 146 | } 147 | 148 | out = out[:int(count)] 149 | return &object.StringObject{Value: string(out)} 150 | } 151 | 152 | // RIGHT returns the N right-most characters of the string. 153 | func RIGHT(env Environment, args []object.Object) object.Object { 154 | 155 | // Get the (string) argument. 156 | if args[0].Type() != object.STRING { 157 | return object.Error("Wrong type") 158 | } 159 | 160 | // We convert this to an array of runes because we 161 | // want to handle unicode strings. 162 | in := []rune(args[0].(*object.StringObject).Value) 163 | 164 | // Get the (float) argument. 165 | if args[1].Type() != object.NUMBER { 166 | return object.Error("Wrong type") 167 | } 168 | n := int(args[1].(*object.NumberObject).Value) 169 | 170 | // ensure it is positive 171 | if n < 0 { 172 | return object.Error("Positive argument only") 173 | } 174 | 175 | if n > len(in) { 176 | n = len(in) 177 | } 178 | right := in[len(in)-int(n):] 179 | 180 | return &object.StringObject{Value: string(right)} 181 | } 182 | 183 | // SPC returns a string containing the given number of spaces 184 | func SPC(env Environment, args []object.Object) object.Object { 185 | 186 | // Get the (float) argument. 187 | if args[0].Type() != object.NUMBER { 188 | return object.Error("Wrong type") 189 | } 190 | n := int(args[0].(*object.NumberObject).Value) 191 | 192 | // ensure it is positive 193 | if n < 0 { 194 | return object.Error("Positive argument only") 195 | } 196 | 197 | s := "" 198 | for i := 0; i < n; i++ { 199 | s += " " 200 | } 201 | 202 | return &object.StringObject{Value: s} 203 | } 204 | 205 | // STR converts a number to a string 206 | func STR(env Environment, args []object.Object) object.Object { 207 | 208 | // Error? 209 | if args[0].Type() == object.ERROR { 210 | return args[0] 211 | } 212 | 213 | // Already a string? 214 | if args[0].Type() == object.STRING { 215 | return args[0] 216 | } 217 | 218 | // Get the value 219 | i := args[0].(*object.NumberObject).Value 220 | s := "" 221 | 222 | if i == float64(int(i)) { 223 | s = fmt.Sprintf("%d", int(i)) 224 | } else { 225 | s = fmt.Sprintf("%f", i) 226 | } 227 | return &object.StringObject{Value: s} 228 | } 229 | 230 | // TL returns a string, minus the first character. 231 | func TL(env Environment, args []object.Object) object.Object { 232 | 233 | // Get the (string) argument. 234 | if args[0].Type() != object.STRING { 235 | return object.Error("Wrong type") 236 | } 237 | 238 | // We convert this to an array of runes because we 239 | // want to handle unicode strings. 240 | in := []rune(args[0].(*object.StringObject).Value) 241 | 242 | if len(in) > 1 { 243 | rest := in[1:] 244 | 245 | return &object.StringObject{Value: string(rest)} 246 | } 247 | return &object.StringObject{Value: ""} 248 | } 249 | 250 | // VAL converts a string to a number 251 | func VAL(env Environment, args []object.Object) object.Object { 252 | 253 | // Error? 254 | if args[0].Type() == object.ERROR { 255 | return args[0] 256 | } 257 | 258 | // Already a number? 259 | if args[0].Type() == object.NUMBER { 260 | return args[0] 261 | } 262 | 263 | // Get the value 264 | s := args[0].(*object.StringObject).Value 265 | b, err := strconv.ParseFloat(s, 64) 266 | if err != nil { 267 | return object.Error("VAL: %s", err.Error()) 268 | } 269 | 270 | return &object.NumberObject{Value: float64(b)} 271 | } 272 | -------------------------------------------------------------------------------- /cpu.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "basicbots/delay" 5 | "basicbots/eval" 6 | "basicbots/tokenizer" 7 | "errors" 8 | "flag" 9 | "fmt" 10 | 11 | "math/rand/v2" 12 | "os" 13 | "path/filepath" 14 | ) 15 | 16 | // Reset all robots to default values. 17 | // Reset robots between matches. 18 | func ResetRobots() error { 19 | 20 | // Cheesy way to scamble a array 21 | // Used for randomizing the starting location of robots. 22 | pp := make([]int, 4) 23 | for i := 0; i < 4; i++ { 24 | pp[i] = i 25 | } 26 | 27 | // 25 rounds of swaps 28 | for i := 0; i < 25; i++ { 29 | var s1, s2 int 30 | s1 = rand.IntN(4) 31 | for { 32 | s2 = rand.IntN(4) 33 | if s2 != s1 { 34 | break 35 | } 36 | } 37 | swap1 := pp[s1] 38 | swap2 := pp[s2] 39 | pp[s2] = swap1 40 | pp[s1] = swap2 41 | } 42 | 43 | // quick fix for starting bots off as dead if they are not used. 44 | 45 | // Reset / Init all robots. 46 | for i := 0; i < numberOfRobots; i++ { 47 | Robots[i].Damage = 0 48 | Robots[i].Status = ALIVE 49 | Robots[i].Heading = 0.0 50 | Robots[i].HeadingWanted = Robots[i].Heading 51 | Robots[i].Speed = 0.0 52 | Robots[i].SpeedWanted = Robots[i].Speed 53 | Robots[i].SpeedHold = 0.0 54 | Robots[i].Distance = 0.0 55 | Robots[i].Reload = 0 56 | Robots[i].Scan = 0 57 | Robots[i].Width = 2 58 | Robots[i].Cannon = 0 59 | 60 | // Place the robots on the battle field. 61 | // Each robot is placed in a corner of the battlefield. 62 | switch pp[i] { 63 | case 0: // Upper Left 64 | Robots[i].X = float64(rand.IntN(100)) + 100.0 65 | Robots[i].Y = float64(rand.IntN(100)) + 100.0 66 | case 1: // Lower Right 67 | Robots[i].X = float64(rand.IntN(100)) + 800.0 68 | Robots[i].Y = float64(rand.IntN(100)) + 800.0 69 | case 2: // Upper Right 70 | Robots[i].X = float64(rand.IntN(100)) + 800.0 71 | Robots[i].Y = float64(rand.IntN(100)) + 100.0 72 | case 3: // Lower Left 73 | Robots[i].X = float64(rand.IntN(100)) + 100.0 74 | Robots[i].Y = float64(rand.IntN(100)) + 800.0 75 | } 76 | 77 | // make sure the origins for movement is set 78 | Robots[i].XOrigin = Robots[i].X 79 | Robots[i].YOrigin = Robots[i].Y 80 | 81 | // Set all missiles to default. 82 | for m := 0; m < MAXMISSILES; m++ { 83 | Missiles[i][m].Status = AVAILABLE 84 | Missiles[i][m].Reload = 0 85 | } 86 | 87 | } 88 | 89 | return nil 90 | } 91 | 92 | // Initialize the robots. 93 | // Used to load the program, reset eval and token. 94 | func InitRobots() error { 95 | var err error 96 | 97 | // reset the structs for the robots. 98 | err = ResetRobots() 99 | if err != nil { 100 | return err 101 | } 102 | 103 | // The next two are needed to reset the Interpreter. 104 | // Clear the previous slice if any exist 105 | if len(evaluator) != 0 { 106 | evaluator = evaluator[:0] 107 | } 108 | 109 | // Clear the previous slice if any exsist 110 | if len(token) != 0 { 111 | token = token[:0] 112 | } 113 | 114 | // Loop and load each robots source and initialize eval,token and customer basic functions. 115 | for i := 0; i < numberOfRobots; i++ { 116 | 117 | /* 118 | robotFileName := filepath.Base(robotFileNameWithPath) 119 | robotFileNameNoExt := robotFileName[:len(robotFileName)-len(filepath.Ext(robotFileName))] 120 | robotOutput := "logs/" + robotFileNameNoExt + ".out" 121 | robotDebug1 := "logs/" + robotFileNameNoExt + ".d1" 122 | robotDebug2 := "logs/" + robotFileNameNoExt + ".d2" 123 | */ 124 | var robotFileNameWithPath string 125 | if bench { // need to load saved robot from constansts 126 | robotFileNameWithPath = "testbots/bench2.bas" 127 | } else { // normal set the path to the cli arguments. 128 | robotFileNameWithPath = flag.Args()[i] 129 | } 130 | if len(Robots[i].Program) == 0 { 131 | if bench { // set the program slice to the stored robot. 132 | Robots[i].Program = []byte(benchbot) 133 | } else { // load the program from the file set previous. 134 | Robots[i].Program, err = os.ReadFile(robotFileNameWithPath) 135 | } 136 | if err != nil { 137 | fmt.Fprintf(os.Stderr, "Could not load '%s'\n", flag.Args()[i]) 138 | os.Exit(1) 139 | } 140 | 141 | // parse the full filename and path to return only the filename. Needed for battlescreen and final output. 142 | Robots[i].Name = filepath.Base(robotFileNameWithPath) 143 | 144 | } 145 | 146 | // Create tokens for the robots source. Tokenize 147 | tt := tokenizer.New(string(Robots[i].Program)) 148 | token = append(token, tt) 149 | 150 | // Create new eval for the robot. 151 | ee, err := eval.New(token[i]) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | /* 157 | if debug { 158 | fmt.Printf("Robot:%d:%s InitRobot X:%5.2f Y:%5.2f\n", i, Robots[i].Name, Robots[i].X, Robots[i].Y) 159 | } 160 | */ 161 | if trace { 162 | if i == 1 { 163 | ee.SetTrace(true) 164 | } 165 | } 166 | 167 | // Add the custom funcitons of basicbots to the eval 168 | ee.RegisterBuiltin("LOCX", 0, FunctionLocX) 169 | ee.RegisterBuiltin("LOCY", 0, FunctionLocY) 170 | ee.RegisterBuiltin("SPEED", 0, FunctionSpeed) 171 | ee.RegisterBuiltin("DAMAGE", 0, FunctionDamage) 172 | ee.RegisterBuiltin("DRIVE", 2, FunctionDrive) 173 | ee.RegisterBuiltin("SCAN", 2, FunctionScan) 174 | ee.RegisterBuiltin("CANNON", 2, FunctionCannon) 175 | ee.RegisterBuiltin("IN", 0, FunctionIn) 176 | ee.RegisterBuiltin("OUT", 1, FunctionOut) 177 | ee.RegisterBuiltin("STRC$", 2, FunctionSTRC) // STRC , 178 | ee.RegisterBuiltin("TEAM", 0, FunctionTeam) 179 | evaluator = append(evaluator, ee) 180 | 181 | } 182 | 183 | return nil 184 | } 185 | 186 | // Main loop for executing the code of the robots, triggers & movement. 187 | func RunRobots() error { 188 | 189 | if battledisplay { 190 | scr.Show() 191 | } 192 | 193 | if battledisplay { 194 | go eventloop() 195 | } 196 | 197 | cycles = 0 198 | 199 | for { 200 | //if timingTest { 201 | // startTime = time.Now() 202 | //} 203 | 204 | // if battlediplay flag set then update the display 205 | if battledisplay { 206 | if cycles%30 == 0 { 207 | /* 208 | if timingTest { 209 | timeBucket = time.Since(startTime) 210 | } 211 | */ 212 | plotbattlefield() // put screan changes into the buffer 213 | scr.Show() // move the buffer to the screen 214 | /* 215 | if timingTest { 216 | startTime = time.Now() 217 | } 218 | */ 219 | } 220 | } 221 | 222 | // handle esc key 223 | select { 224 | case etype = <-event: // etype is sent from event loop in display.go 225 | 226 | default: // kep the channel from blocking 227 | } 228 | 229 | // escape key code, break out of the game loop if set 230 | if etype == 99 { 231 | break 232 | } 233 | 234 | // run a step for each robot current hold the current robot, only let 235 | // the for loop change the value of current 236 | for current = 0; current < numberOfRobots; current++ { 237 | checkAlive(current) // is the robot alive? 238 | if Robots[current].Status == DEAD { 239 | continue // skip this robot 240 | } 241 | 242 | // is reload finished? subtract 1 if not 243 | if Robots[current].Reload > 0 { 244 | Robots[current].Reload-- 245 | } 246 | 247 | // run 1 line of basic code for this robot 248 | err := evaluator[current].RunStep() 249 | if err != nil { 250 | etype = BASICERROR // needed to make sure the tcell display is closed before exiting the loop . function 251 | em := fmt.Sprintf("Error running program:\n\t%s\n", err.Error()) 252 | return errors.New(em) 253 | } 254 | 255 | // if the current robots programs has ended, kill the robot. 256 | if evaluator[current].ProgramEnd { 257 | Robots[current].Damage = 100 258 | Robots[current].Status = DEAD 259 | continue // dont end battlebots if a single program ends. 260 | } 261 | 262 | } 263 | 264 | // check if we should exit the main game loop 265 | // primaryly from win,tie,lose is ready. 266 | if endCondition() { 267 | break 268 | } 269 | 270 | // increment cycles. 271 | cycles++ 272 | 273 | // end of simulation because we reached the maxcycles? 274 | if cycles == maxCycles { 275 | for nn := 0; nn < numberOfRobots; nn++ { 276 | if Robots[nn].Status == ALIVE { // this may be wrong. need closer inspection. 277 | Robots[nn].Tie++ 278 | Robots[nn].Points++ 279 | } 280 | } 281 | if debug { 282 | fmt.Fprintln(os.Stderr, "Cycle limit has been reached.") 283 | } 284 | break 285 | } 286 | 287 | // Time to update movement 288 | if cycles%MOVECLICKS == 0 { 289 | moverobot() 290 | movemissile() 291 | } 292 | 293 | // Pause for needed time to slow down the battledisplay 294 | if battledisplay { 295 | delay.Delay(cycledelay) 296 | } 297 | } 298 | 299 | endGame() 300 | 301 | return nil 302 | } 303 | -------------------------------------------------------------------------------- /delay/ddelay.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | 3 | /* delay 4 | Unix code for timing delay. Only included when OS is linux. 5 | Sleep is not accurate and the delay needs to be accurate for setting the number of times the battlescreen is updated, thus setting the speed of the game. 6 | */ 7 | package delay 8 | 9 | import "time" 10 | 11 | // Delay : delay for d nanoseconds 12 | func Delay(d int64) { 13 | time.Sleep(time.Duration(d) * time.Nanosecond) 14 | /* 15 | var t syscall.Timespec 16 | var b syscall.Timespec 17 | t.Sec = 0 18 | t.Nsec = d 19 | //starttime := time.Now() 20 | //for i := 0; i < 1000; i++ { 21 | _ = syscall.Nanosleep(&t, &b) 22 | // _ = syscall.Times 23 | //} 24 | //d := time.Since(starttime) 25 | //fmt.Println("E", d) 26 | //fmt.Println(b) 27 | */ 28 | } 29 | -------------------------------------------------------------------------------- /delay/udelay.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | /* delay 4 | Unix code for timing delay. Only included when OS is linux. 5 | */ 6 | package delay 7 | 8 | import ( 9 | "syscall" 10 | ) 11 | 12 | // Delay : delay for d nanoseconds 13 | func Delay(d int64) { 14 | var t syscall.Timespec 15 | var b syscall.Timespec 16 | t.Sec = 0 17 | t.Nsec = d 18 | //starttime := time.Now() 19 | //for i := 0; i < 1000; i++ { 20 | _ = syscall.Nanosleep(&t, &b) 21 | // _ = syscall.Times 22 | //} 23 | //d := time.Since(starttime) 24 | //fmt.Println("E", d) 25 | //fmt.Println(b) 26 | } 27 | -------------------------------------------------------------------------------- /delay/wdelay.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | /* delay 4 | Windows code for timing delay. Only included when OS is windows. 5 | Sleep is not accurate and the delay needs to be accurate for setting the number of times the battlescreen is updated, thus setting the speed of the game. 6 | */ 7 | package delay 8 | 9 | import ( 10 | "syscall" 11 | "time" 12 | "unsafe" 13 | ) 14 | 15 | // precision timing 16 | var ( 17 | modkernel32 = syscall.NewLazyDLL("kernel32.dll") 18 | procFreq = modkernel32.NewProc("QueryPerformanceFrequency") 19 | procCounter = modkernel32.NewProc("QueryPerformanceCounter") 20 | 21 | qpcFrequency = getFrequency() 22 | qpcBase = getCount() 23 | ) 24 | 25 | // getFrequency returns frequency in ticks per second. 26 | func getFrequency() int64 { 27 | var freq int64 28 | r1, _, _ := syscall.Syscall(procFreq.Addr(), 1, uintptr(unsafe.Pointer(&freq)), 0, 0) 29 | if r1 == 0 { 30 | panic("call failed") 31 | } 32 | return freq 33 | } 34 | 35 | // getCount returns counter ticks. 36 | func getCount() int64 { 37 | var qpc int64 38 | syscall.Syscall(procCounter.Addr(), 1, uintptr(unsafe.Pointer(&qpc)), 0, 0) 39 | return qpc 40 | } 41 | 42 | // Now returns current time.Duration with best possible precision. 43 | // 44 | // Now returns time offset from a specific time. 45 | // The values aren't comparable between computer restarts or between computers. 46 | func now() time.Duration { 47 | return time.Duration(getCount()-qpcBase) * time.Second / (time.Duration(qpcFrequency) * time.Nanosecond) 48 | } 49 | 50 | // NowPrecision returns maximum possible precision for Now in nanoseconds. 51 | //func nowPrecision() float64 { 52 | // return float64(time.Second) / (float64(qpcFrequency) * float64(time.Nanosecond)) 53 | //} 54 | 55 | // Delay : delay for d nanoseconds 56 | func Delay(d int64) { 57 | var st, rn int64 58 | st = int64(now()) 59 | for { 60 | rn = int64(now()) 61 | if rn-st > d { 62 | return 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /dev-docs/basicbots-dev.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # basicbots 4 | 5 | ```go 6 | import "basicbots" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [Constants](<#constants>) 12 | - [Variables](<#variables>) 13 | - [func FunctionCannon(env builtin.Environment, args []object.Object) object.Object](<#func-functioncannon>) 14 | - [func FunctionDamage(env builtin.Environment, args []object.Object) object.Object](<#func-functiondamage>) 15 | - [func FunctionDrive(env builtin.Environment, args []object.Object) object.Object](<#func-functiondrive>) 16 | - [func FunctionLocX(env builtin.Environment, args []object.Object) object.Object](<#func-functionlocx>) 17 | - [func FunctionLocY(env builtin.Environment, args []object.Object) object.Object](<#func-functionlocy>) 18 | - [func FunctionScan(env builtin.Environment, args []object.Object) object.Object](<#func-functionscan>) 19 | - [func FunctionSpeed(env builtin.Environment, args []object.Object) object.Object](<#func-functionspeed>) 20 | - [func GetAngle(x1, y1, x2, y2 float64) float64](<#func-getangle>) 21 | - [func GetDistance(x1, y1, x2, y2 float64) float64](<#func-getdistance>) 22 | - [func InitRobots() error](<#func-initrobots>) 23 | - [func ResetRobots() error](<#func-resetrobots>) 24 | - [func RunRobots() error](<#func-runrobots>) 25 | - [func Scanner(angle, width, b1x, b1y, b2x, b2y float64) float64](<#func-scanner>) 26 | - [func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string)](<#func-drawbox>) 27 | - [func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string)](<#func-drawtext>) 28 | - [func eventloop()](<#func-eventloop>) 29 | - [func initDisplay() error](<#func-initdisplay>) 30 | - [func main()](<#func-main>) 31 | - [func movemissile()](<#func-movemissile>) 32 | - [func moverobot()](<#func-moverobot>) 33 | - [func plotbattlefield()](<#func-plotbattlefield>) 34 | - [type Missile](<#type-missile>) 35 | - [type Robot](<#type-robot>) 36 | 37 | 38 | ## Constants 39 | 40 | Constants for battlefield\. 41 | 42 | ```go 43 | const ( 44 | MAXX = 1000.0 // MAXX : Maximum size of the battlefield in the X direction. 45 | MAXY = 1000.0 // MAXY : Maximum size of the battlefield in the Y direction. 46 | ) 47 | ``` 48 | 49 | Constants for robot status\. 50 | 51 | ```go 52 | const ( 53 | MAXROBOTS = 4 // MAXROBOTS : The maximum number of robots allowed. 54 | ALIVE = 0 // ALIVE : Robot is functional. 55 | DEAD = 1 // DEAD : Robot is dead. 56 | ACCEL = 10.0 // ACCEL : Max Acceleration. 57 | ) 58 | ``` 59 | 60 | Constants for converting degrees to radians and back\. 61 | 62 | ```go 63 | const ( 64 | DEG2RAD = 0.0174532925 // DEG2RAD : multiply 0-360 degrees to get radians. 65 | RAD2DEG = 57.2957795130 // RAD2GEG : multiply radians to get degrees. 66 | ) 67 | ``` 68 | 69 | Constants for missile reload and movement\. 70 | 71 | ```go 72 | const ( 73 | MAXMISSILES = 2 // MAXMISSILES : Maximum number if missiles a robot can have on the battlefield at one time. 74 | MISSLERANGE = 700.0 // MISSLERANGE : Maxumum range of a missile. 75 | RELOAD = 15 // RELOAD : Number of of movrment cycles for a missile reload. 76 | MISSILESPEED = 500.0 // MISSILESPEED : Missiles move at full speed. No ramp up. 500 is 500% vs 100% for robots. 77 | ROBOTRELOAD = 5 // ROBOTRELOAD : Number of cycles for the robot to reload. Used to slow down the firing of the second missile. 78 | EXPLODECOUNT = 5 // EXPLODECOUNT : The number of momvement cycles for the explosion to show in the battlescreen. 79 | ) 80 | ``` 81 | 82 | Constants for missile status 83 | 84 | ```go 85 | const ( 86 | AVAILABLE = 1 // AVAILABLE : Missle is available for firing 87 | FLYING = 2 // FLYING : Missile is in flight. 88 | EXPLODE = 3 // EXPLODE : Missile is the process of exploding. 89 | EXPLODING = 4 // EXPLODING : Blowing up 90 | ) 91 | ``` 92 | 93 | Constants for robots running into things\. 94 | 95 | ```go 96 | const ( 97 | DAMAGEWALL = 2 // DAMAGEWALL : Amount of damage a robot will occure when it hits a wall. 98 | DAMAGECOL = 4 // DAMAGECOL : Amount of damage a robot will occure when it hits another robot. Both robots take damamge. 99 | ) 100 | ``` 101 | 102 | Constants for missle dmaage and blast radius\. 103 | 104 | ```go 105 | const ( 106 | MISFAR = 40 // MISFAR : Largest blast radius to cause damage. 107 | MISNEAR = 20 // MISNEAR : Medium blast radius to cause damage. 108 | MISCLOSE = 5 // MISCLOSE : Closest blast radius to cause damage. 109 | DAMFAR = 3 // DAMFAR : Damage to robot at between MISNEAR and MISFAR 110 | DAMNEAR = 5 // DAMNEAR : Damage to robot at between MISNEAR and MISCLOSE 111 | DAMCLOSE = 10 // DAMCLOSE : Damage to robot when at or below MISCLOSE 112 | ) 113 | ``` 114 | 115 | Constants for cycles routines\. Mainly movements\. 116 | 117 | ```go 118 | const ( 119 | MOVECLICKS = 50 // CLICKS : Number of cycles between moverobot and movemissile. 120 | 121 | ) 122 | ``` 123 | 124 | ## Variables 125 | 126 | ```go 127 | var Missiles [MAXROBOTS][MAXMISSILES]Missile // Missile : Array of the missiles that can be used. 128 | ``` 129 | 130 | ```go 131 | var Robots []Robot // Robots : Array of the robots 132 | ``` 133 | 134 | ```go 135 | var battleSizeX int // battleSizeX : Size of the battlescreen on the console in the X direction. 136 | ``` 137 | 138 | ```go 139 | var battleSizeY int // battleSizeY : Size of the battlescreen on the console in the Y direction. 140 | ``` 141 | 142 | ```go 143 | var battledisplay bool // battledisplay : true show graphics 144 | ``` 145 | 146 | ```go 147 | var boxStyle tcell.Style // boxStyle : Leagacy purple 148 | ``` 149 | 150 | ```go 151 | var current int // current : The current active robot 152 | ``` 153 | 154 | ```go 155 | var cycledelay int64 // cycledelay : Delay in nanoseconds. Used in the battlescreen mode to slow down the play. 156 | ``` 157 | 158 | ```go 159 | var cycles int // cycles : The number of cpu cycles 160 | ``` 161 | 162 | variables 163 | 164 | ```go 165 | var debug bool // debug : Debug flag 166 | ``` 167 | 168 | ```go 169 | var defStyle tcell.Style // defStyle : white on blank for text. Used in tcell. 170 | ``` 171 | 172 | ```go 173 | var evaluator []*eval.Interpreter // evaluator : Slice of Interpreters. 174 | ``` 175 | 176 | ```go 177 | var event = make(chan int) // event : Channel for getting out of tcell with escape key. 178 | ``` 179 | 180 | ```go 181 | var lox float64 // lox : scaling factor for battlefield to console area. 182 | ``` 183 | 184 | ```go 185 | var loy float64 // loy : scalling factor for battlefield to console area. 186 | ``` 187 | 188 | ```go 189 | var matchcount int // matchcount : Number of matches to play with current robots. Can not be used with 'battledisplay'. 190 | ``` 191 | 192 | ```go 193 | var maxCycles int // maxCycles : Maximum numer of cycles per match. Can change with cli flag. 194 | ``` 195 | 196 | ```go 197 | var numberOfRobots int // numberOfRobots : The number of robots in this simulation 198 | ``` 199 | 200 | ```go 201 | var scr tcell.Screen // scr : tcell screen interface. Using global to keep from haveing go routines. 202 | ``` 203 | 204 | ```go 205 | var token []*tokenizer.Tokenizer // token : slice of tokienizers 206 | ``` 207 | 208 | ```go 209 | var trace bool // trace : Trace flag 210 | ``` 211 | 212 | ## func FunctionCannon 213 | 214 | ```go 215 | func FunctionCannon(env builtin.Environment, args []object.Object) object.Object 216 | ``` 217 | 218 | FunctionCannon : Basic statement\. CANNON direction\, range\. Fire the cannon at angle and distance\. Do nothing if no missiles available\. 219 | 220 | ## func FunctionDamage 221 | 222 | ```go 223 | func FunctionDamage(env builtin.Environment, args []object.Object) object.Object 224 | ``` 225 | 226 | FunctionDamage : Basic statement\. DAMAGE returns the current damage of the robot 227 | 228 | ## func FunctionDrive 229 | 230 | ```go 231 | func FunctionDrive(env builtin.Environment, args []object.Object) object.Object 232 | ``` 233 | 234 | FunctionDrive : Basic statement\. DRIVE direction\,speed sets speed and direction 235 | 236 | ## func FunctionLocX 237 | 238 | ```go 239 | func FunctionLocX(env builtin.Environment, args []object.Object) object.Object 240 | ``` 241 | 242 | FunctionLocX : Basic statement\. LOCX returns the current Y location\. 243 | 244 | ## func FunctionLocY 245 | 246 | ```go 247 | func FunctionLocY(env builtin.Environment, args []object.Object) object.Object 248 | ``` 249 | 250 | FunctionLocY : Basic statement\. LOCY returns the current Y location 251 | 252 | ## func FunctionScan 253 | 254 | ```go 255 | func FunctionScan(env builtin.Environment, args []object.Object) object.Object 256 | ``` 257 | 258 | FunctionScan : Basic statement\. SCAN direction\,width\. Scan the battlefield in direction with a width of \+/\- width 259 | 260 | ## func FunctionSpeed 261 | 262 | ```go 263 | func FunctionSpeed(env builtin.Environment, args []object.Object) object.Object 264 | ``` 265 | 266 | FunctionSpeed : Basic statement\. SPEED returns the current speed of the robot 267 | 268 | ## func GetAngle 269 | 270 | ```go 271 | func GetAngle(x1, y1, x2, y2 float64) float64 272 | ``` 273 | 274 | GetAngle : Return the angle in degrees from x1\,y1 to x2\,y2 275 | 276 | ## func GetDistance 277 | 278 | ```go 279 | func GetDistance(x1, y1, x2, y2 float64) float64 280 | ``` 281 | 282 | GetDistance : Return the distance between x1\,y1 to x2\,y2 283 | 284 | ## func InitRobots 285 | 286 | ```go 287 | func InitRobots() error 288 | ``` 289 | 290 | ## func ResetRobots 291 | 292 | ```go 293 | func ResetRobots() error 294 | ``` 295 | 296 | ## func RunRobots 297 | 298 | ```go 299 | func RunRobots() error 300 | ``` 301 | 302 | ## func Scanner 303 | 304 | ```go 305 | func Scanner(angle, width, b1x, b1y, b2x, b2y float64) float64 306 | ``` 307 | 308 | Scanner : Scan angle with within x\,y of both robots\. Returns distance if robot found or 0 if none found\. 309 | 310 | ## func drawBox 311 | 312 | ```go 313 | func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) 314 | ``` 315 | 316 | drawBox : Draws a box bording x1\,y1 & x2\,y2\. Uses style for forground and background colors\. text puts a string in the upper left corner od the box\. 317 | 318 | ## func drawText 319 | 320 | ```go 321 | func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) 322 | ``` 323 | 324 | drawText : Puts text onthe screen at x1\,y1 to x2\,y2 using style for forground and background colors\. 325 | 326 | ## func eventloop 327 | 328 | ```go 329 | func eventloop() 330 | ``` 331 | 332 | evenloop : go routine for processing tcell events\. Channel even passes back to main program to terminate\. 333 | 334 | ## func initDisplay 335 | 336 | ```go 337 | func initDisplay() error 338 | ``` 339 | 340 | initDisplay : Initialize tcell 341 | 342 | ## func main 343 | 344 | ```go 345 | func main() 346 | ``` 347 | 348 | ## func movemissile 349 | 350 | ```go 351 | func movemissile() 352 | ``` 353 | 354 | movemissile : move all flying missiles per motion click\. Check for blast damage\, htting wall\, etc\. 355 | 356 | ## func moverobot 357 | 358 | ```go 359 | func moverobot() 360 | ``` 361 | 362 | moverobot : Move robots every motion cycle\. Take damage\, detect dead robots\, detect for collision\. 363 | 364 | ## func plotbattlefield 365 | 366 | ```go 367 | func plotbattlefield() 368 | ``` 369 | 370 | plotbattlefield : Erase and Draw the robots\, missiles and blasts on the battlefield as well as the side status\. 371 | 372 | ## type Missile 373 | 374 | Missle : type struct for holding missle variables\. 375 | 376 | ```go 377 | type Missile struct { 378 | X float64 // X : Current X location of the missile. 379 | Y float64 // Y : Current Y location of the missile. 380 | XOrigin float64 // XOrigin : Origin point from the last X location. Used in movement for directions. 381 | YOrigin float64 // YOrigin : Origin point from the lasy Y location. Used in movement for ditections. 382 | XO float64 // XO : Origin point from the last X location. Used in movement for directions. 383 | YO float64 // YO : Origin point from the lasy Y location. Used in movement for ditections. 384 | XPlotOld int // XPlotOld : Last battlefield X position. Used for erasing the old marker. 385 | YPlotOld int // YPlotOld : Last battlefield Y position. Used for erasing the old marker. 386 | Heading float64 // Heading : Heading of the missile. 387 | Distance float64 // Distance : The set distance at which the missile explodes. 388 | Status int // Status : The status of the missile. Available,flying,exploding. 389 | Reload int // Reload : Movement clicks until reloaded. 390 | ExplodeCount int // ExplodeCount : Time in motion cycles to have explosion showing. 391 | } 392 | ``` 393 | 394 | ## type Robot 395 | 396 | Robot : type struct for holding robot variables 397 | 398 | ```go 399 | type Robot struct { 400 | Name string // Name : The name of the robot 401 | 402 | X float64 // X : Current X of the robot. 403 | Y float64 // Y : Current Y of the robot. 404 | XOrigin float64 // XOrigin : orgin x location. Used in movement. 405 | YOrigin float64 // YOrigin : orgin y location. Used in movement. 406 | XPlotOld int // XPlotOld : Last X plot on the battlefield. Used to remove the past marker. 407 | YPlotOld int // YPlotOld : Last Y plot on the battlefield. Used to remove the past marker. 408 | 409 | Damage int // Damage : Current state of the damage of the robot 410 | 411 | Speed float64 // Speed : Current speed of the robot. 412 | SpeedWanted float64 // SpeedWanted : The desired speed of the robot. 413 | SpeedHold float64 // SpeedHold : Holds the previous speed while in a turn. 414 | 415 | Heading float64 // Heading : Current heading of the robot 416 | HeadingWanted float64 // HeadingWanted : The disired heading of the robot. 417 | Distance float64 // Distance : The Distance the robot has traveled from the origin point. 418 | 419 | Scan float64 // Scan : Scan heading. 0-360 420 | Width float64 // Width : Scan width 2-10 421 | 422 | Cannon float64 // Cannon : Vannon heading 423 | Reload int // Reload : Countdown until cannon reload is complete 424 | 425 | Status int // Status : Status of the robot. Dead or Alive. 426 | 427 | Winner int // Winner : Number of wins 428 | Lose int // Lose : Number off times lost 429 | Tie int // Tie : Number of times tied 430 | 431 | Program []byte // Program : Byte slice holding the program's text file. 432 | } 433 | ``` 434 | 435 | 436 | 437 | Generated by [gomarkdoc]() 438 | -------------------------------------------------------------------------------- /display.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/gdamore/tcell/v2" 8 | ) 9 | 10 | // Initialize tcell 11 | func initDisplay() error { 12 | var err error 13 | defStyle = tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset) 14 | //boxStyle = tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorPurple) 15 | 16 | // Creat a new tcell screen 17 | scr, err = tcell.NewScreen() 18 | if err != nil { 19 | return err // Something went wrong, pass the error down stream. 20 | } 21 | 22 | // Init the tcell screen 23 | if err = scr.Init(); err != nil { 24 | return err 25 | } 26 | 27 | // set the default style, white on black screen. 28 | scr.SetStyle(defStyle) 29 | scr.Clear() 30 | 31 | // Draw initial boxes 32 | battleSizeX = 80 - 20 33 | battleSizeY = 23 34 | drawBox(scr, 0, 0, battleSizeX, battleSizeY, defStyle, "Battlefield") 35 | lox = (float64(battleSizeX) - 2.0) / MAXX 36 | loy = (float64(battleSizeY) - 2.0) / MAXY 37 | 38 | return nil 39 | } 40 | 41 | // Draws a box bording x1,y1 & x2,y2. Uses style for forground and background colors. text puts a string in the upper left corner od the box. 42 | func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) { 43 | if y2 < y1 { 44 | y1, y2 = y2, y1 45 | } 46 | if x2 < x1 { 47 | x1, x2 = x2, x1 48 | } 49 | 50 | // Fill background 51 | for row := y1; row <= y2; row++ { 52 | for col := x1; col <= x2; col++ { 53 | s.SetContent(col, row, ' ', nil, style) 54 | } 55 | } 56 | 57 | // Draw borders 58 | for col := x1; col <= x2; col++ { 59 | s.SetContent(col, y1, tcell.RuneHLine, nil, style) 60 | s.SetContent(col, y2, tcell.RuneHLine, nil, style) 61 | } 62 | for row := y1 + 1; row < y2; row++ { 63 | s.SetContent(x1, row, tcell.RuneVLine, nil, style) 64 | s.SetContent(x2, row, tcell.RuneVLine, nil, style) 65 | } 66 | 67 | // Only draw corners if necessary 68 | if y1 != y2 && x1 != x2 { 69 | s.SetContent(x1, y1, tcell.RuneULCorner, nil, style) 70 | s.SetContent(x2, y1, tcell.RuneURCorner, nil, style) 71 | s.SetContent(x1, y2, tcell.RuneLLCorner, nil, style) 72 | s.SetContent(x2, y2, tcell.RuneLRCorner, nil, style) 73 | } 74 | 75 | //drawText(s, x1+1, y1+1, x2-1, y2-1, style, text) 76 | drawText(s, x1+2, y1, x1+len(text)+2, y2, style, text) 77 | } 78 | 79 | // Puts text onthe screen at x1,y1 to x2,y2 using style for forground and background colors. 80 | func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) { 81 | row := y1 82 | col := x1 83 | for _, r := range text { 84 | s.SetContent(col, row, r, nil, style) 85 | col++ 86 | if col >= x2 { 87 | row++ 88 | col = x1 89 | } 90 | if row > y2 { 91 | break 92 | } 93 | } 94 | } 95 | 96 | // Go routine for processing tcell events. Channel 'event' passes 97 | // back to main program to terminate. 98 | func eventloop() { 99 | for { 100 | ev := scr.PollEvent() 101 | switch ev := ev.(type) { 102 | case *tcell.EventKey: 103 | if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC { 104 | event <- ESCKEY // Escape key 105 | break 106 | } 107 | } 108 | } 109 | 110 | } 111 | 112 | // Erase and Draw the robots, missiles and blasts on the 113 | // battlefield as well as the side status. 114 | func plotbattlefield() { 115 | posx := battleSizeX + 2 116 | var posy int 117 | 118 | for r := 0; r < numberOfRobots; r++ { 119 | 120 | posy = 5 * r // Y position of each robots running stats. 5 lines per robot 121 | 122 | checkAlive(r) 123 | //if Robots[r].Status == DEAD { 124 | // continue 125 | //} 126 | 127 | if Robots[r].XPlotOld != 0 && Robots[r].YPlotOld != 0 { 128 | tg := rune(' ') 129 | scr.SetCell(Robots[r].XPlotOld, Robots[r].YPlotOld, defStyle, tg) 130 | } 131 | 132 | xf := Robots[r].X * lox 133 | yf := Robots[r].Y * loy 134 | x := int(xf) + 1 135 | y := int(yf) + 1 136 | 137 | if Robots[r].Status != DEAD { 138 | t := []rune(strconv.Itoa(r + 1)) 139 | scr.SetCell(x, y, defStyle, t[0]) 140 | } 141 | Robots[r].XPlotOld = x 142 | Robots[r].YPlotOld = y 143 | 144 | // Missles 145 | 146 | for m := 0; m < MAXMISSILES; m++ { 147 | if Missiles[r][m].Status == FLYING { 148 | mxf := Missiles[r][m].X * lox 149 | myf := Missiles[r][m].Y * loy 150 | mx := int(mxf) + 1 151 | my := int(myf) + 1 152 | if Missiles[r][m].XPlotOld != 0 || Missiles[r][m].YPlotOld != 0 { 153 | tgme := rune(' ') 154 | if isBorder(Missiles[r][m].XPlotOld, Missiles[r][m].YPlotOld) { 155 | scr.SetCell(Missiles[r][m].XPlotOld, Missiles[r][m].YPlotOld, defStyle, tgme) 156 | } 157 | } 158 | tgm := rune('.') 159 | if isBorder(mx, my) { 160 | scr.SetCell(mx, my, defStyle, tgm) 161 | } 162 | Missiles[r][m].XPlotOld = mx 163 | Missiles[r][m].YPlotOld = my 164 | } 165 | if Missiles[r][m].Status == EXPLODE { 166 | mxf := Missiles[r][m].X * lox 167 | myf := Missiles[r][m].Y * loy 168 | mx := int(mxf) + 1 169 | my := int(myf) + 1 170 | // Switching to function 171 | //if Missiles[r][m].XPlotOld != 0 || Missiles[r][m].YPlotOld != 0 { 172 | if isBorder(Missiles[r][m].XPlotOld, Missiles[r][m].YPlotOld) { 173 | tgme := rune(' ') 174 | scr.SetCell(Missiles[r][m].XPlotOld, Missiles[r][m].YPlotOld, defStyle, tgme) 175 | } 176 | //} 177 | tgm := rune('*') 178 | if isBorder(mx, my) { 179 | scr.SetCell(mx, my, defStyle, tgm) 180 | } 181 | Missiles[r][m].XPlotOld = mx 182 | Missiles[r][m].YPlotOld = my 183 | } 184 | 185 | if Missiles[r][m].Status == EXPLODING { 186 | mxf := Missiles[r][m].X * lox 187 | myf := Missiles[r][m].Y * loy 188 | mx := int(mxf) + 1 189 | my := int(myf) + 1 190 | // if Missiles[r][m].XPlotOld != 0 && Missiles[r][m].YPlotOld != 0 { 191 | // tgme := rune(' ') 192 | // scr.SetCell(Missiles[r][m].XPlotOld, Missiles[r][m].YPlotOld, defStyle, tgme) 193 | // } 194 | for j := 0; j < 3; j++ { 195 | for k := 0; k < 3; k++ { 196 | tgm := rune('*') 197 | if isBorder(mx-1+k, my+j) { 198 | scr.SetCell(mx-1+k, my+j, defStyle, tgm) 199 | } 200 | } 201 | } 202 | // tgm := rune('*') 203 | //scr.SetCell(mx, my, defStyle, tgm) 204 | //Missiles[r][m].XPlotOld = mx 205 | // Missiles[r][m].YPlotOld = my 206 | } 207 | 208 | if Missiles[r][m].ExplodeCount == EXPLODECOUNT { 209 | mxf := Missiles[r][m].X * lox 210 | myf := Missiles[r][m].Y * loy 211 | mx := int(mxf) + 1 212 | my := int(myf) + 1 213 | // if Missiles[r][m].XPlotOld != 0 && Missiles[r][m].YPlotOld != 0 { 214 | // tgme := rune(' ') 215 | // scr.SetCell(Missiles[r][m].XPlotOld, Missiles[r][m].YPlotOld, defStyle, tgme) 216 | // } 217 | for j := 0; j < 3; j++ { 218 | for k := 0; k < 3; k++ { 219 | tgm := rune(' ') 220 | if isBorder(mx-1+k, my+j) { 221 | scr.SetCell(mx-1+k, my+j, defStyle, tgm) 222 | } 223 | } 224 | } 225 | // tgm := rune('*') 226 | //scr.SetCell(mx, my, defStyle, tgm) 227 | //Missiles[r][m].XPlotOld = mx 228 | // Missiles[r][m].YPlotOld = my 229 | } 230 | 231 | } 232 | 233 | dd := Robots[r].Name // fmt.Sprintf("%s", Robots[r].Name) 234 | drawText(scr, posx, posy, posx+17, posy, defStyle, dd) 235 | posy++ 236 | dd = fmt.Sprintf("SCN %03d WTH %03d", int(Robots[r].Scan), int(Robots[r].Width)) 237 | drawText(scr, posx, posy, posx+17, posy, defStyle, dd) 238 | posy++ 239 | dd = fmt.Sprintf("HNG %03d SPD %03d", int(Robots[r].Heading), int(Robots[r].Speed)) 240 | drawText(scr, posx, posy, posx+17, posy, defStyle, dd) 241 | posy++ 242 | dd = fmt.Sprintf("CAN %03d DAM %03d", int(Robots[r].Cannon), Robots[r].Damage) 243 | drawText(scr, posx, posy, posx+17, posy, defStyle, dd) 244 | posy++ 245 | dd = fmt.Sprintf(" X %03d Y %03d", int(Robots[r].X), int(Robots[r].Y)) 246 | drawText(scr, posx, posy, posx+17, posy, defStyle, dd) 247 | posy++ 248 | 249 | cycleString := fmt.Sprintf("Cycles %d", cycles) 250 | drawText(scr, posx, battleSizeY-2, posx+len(cycleString)+1, battleSizeY-2, defStyle, cycleString) 251 | 252 | //fps := fmt.Sprintf("%s", timeBucket) 253 | //drawText(scr, posx, battleSizeY-1, posx+len(fps)+1, battleSizeY-1, defStyle, fps) 254 | } 255 | 256 | } 257 | 258 | func isBorder(x, y int) bool { 259 | 260 | if x >= battleSizeX { 261 | return false 262 | } 263 | if x < 1 { 264 | return false 265 | } 266 | 267 | if y >= battleSizeY { 268 | return false 269 | } 270 | if y < 1 { 271 | return false 272 | } 273 | 274 | return true 275 | 276 | } 277 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or advances of any kind 22 | * Trolling, insulting or derogatory comments, and personal or political attacks 23 | * Public or private harassment 24 | * Publishing others' private information, such as a physical or email address, without their explicit permission 25 | * Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Enforcement Responsibilities 28 | 29 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement it. 40 | All complaints will be reviewed and investigated promptly and fairly. 41 | 42 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 43 | 44 | ## Enforcement Guidelines 45 | 46 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 47 | 48 | ### 1. Correction 49 | 50 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 51 | 52 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 53 | 54 | ### 2. Warning 55 | 56 | **Community Impact**: A violation through a single incident or series of actions. 57 | 58 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 59 | 60 | ### 3. Temporary Ban 61 | 62 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 63 | 64 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 65 | 66 | ### 4. Permanent Ban 67 | 68 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 69 | 70 | **Consequence**: A permanent ban from any sort of public interaction within the community. 71 | 72 | ## Attribution 73 | 74 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at 75 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 76 | 77 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 78 | 79 | [homepage]: https://www.contributor-covenant.org 80 | 81 | For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 82 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to **basicbots** 2 | 3 | --- 4 | 5 | First, thank you for contributing to **basicbots**. Without your support and help, this project couldn't exist. 6 | 7 | Table of contents. 8 | 9 | 1. [Code of Conduct](#code-of-conduct) 10 | 2. [Roadmap](#roadmap) 11 | 3. [What should I know before I get started?](#what-should-i-know-before-i-get-started?) 12 | 4. [How Can I Contribute?](#how-can-i-contrubute?) 13 | 5. [Style Guides](#style-guides) 14 | 15 | ## Roadmap 16 | - Current [roadmap](docs/ROADMAP.md) is a combination of bugs, enhancements and longterm goals. 17 | 18 | ## What should I know before I get started? 19 | 20 | - This is all about having fun. If it isn't fun, why are we doing it? 21 | 22 | 23 | ## How Can I Contribute? 24 | - [Coding conventions](#coding-conventions) 25 | - Reporting bugs 26 | - Suggesting Enhancements 27 | - Your First Code Contribution 28 | - Pull Requests 29 | 30 | ## Style Guides 31 | 32 | - Git Commit Messages 33 | - [Effective Go](docs/EFFECTIVE_GO.md) 34 | - The only one needed for now 35 | - [Style Guide 1](docs/STYPE_GUIDE_1.md) 36 | - studing 37 | - [Style Guide 2](docs/STYPE_GUIDE_2.md) 38 | - studing 39 | - Specs Styleguide 40 | - Documentation Styleguide 41 | 42 | --- 43 | 44 | ## Code of Conduct 45 | This project and everyone participating in it is governed by the [**basicbots** Code of Conduct](docs/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to misterunix@gmail.com. 46 | 47 | ## TO DO 48 | 49 | ## Coding conventions 50 | - Start reading our code and you'll get the hang of it. We optimize for readability. 51 | - This is open-source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers, the goal is to make the ride as smooth as possible. 52 | - use gofmt & vet on all source files before pushing. There _should not_ be any errors or warnings. 53 | - use an editor that will automatically perform the gofmt and lint for you. I suggest [VSCode](https://code.visualstudio.com/) with the go extensions. -------------------------------------------------------------------------------- /docs/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTORS 2 | 3 | I would like to thank all the people responsible for making this project the best it can be! 4 | 5 | | github name | twitter name | Contributions | 6 | | :---------- | :----------- | :-----------: | 7 | | [misterunix](https://github.com/misterunix) | [@misterunix](https://twitter.com/misterunix) | :crayon: :pencil: :scroll: :scorpion: :wrench: :bulb: :1st_place_medal: | 8 | | [skx](https://github.com/skx) | [@stolen_souls](https://twitter.com/stolen_souls) | :trophy: | 9 | | [gdamore](https://github.com/gdamore) | ?? | :trophy: | 10 | | [jottinger](https://github.com/jottinger) | [@josephbottinger](https://twitter.com/josephbottinger)| :pencil: :scorpion: :bulb: :crayon: :tada: | 11 | 12 | ### Emoji Key 13 | 14 | | Emoji | Meaning | | Emoji | Meaning | 15 | | :---- | :------ |:-| :---- | :------ | 16 | | :pencil: | Documentation Contributor | | :scroll: | Code Documentation Contributor | 17 | | :scorpion: | Bug Reporter | | :wrench: | Bug Fixer | 18 | | :bulb: | Feature Suggester | | :crayon: | Code Contributor | 19 | | :tada: | Helpful Helper | | | | 20 | | :1st_place_medal: | Admin | | :reminder_ribbon: | Moderator | 21 | | :trophy: | Module Author | | | | 22 | -------------------------------------------------------------------------------- /docs/ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | basicbots roadmap is a combination of 4 | 5 | - bug fixes 6 | - enhancements 7 | - Better error reporting 8 | - ~~`INPUT`~~ `IN` & ~~`PRINT`~~ `OUT` integration for collaboration between two robots. 9 | - More looping routines beyond `FOR`, `NEXT`,`STEP` & `TO` 10 | 11 | 12 | ## long term goals. 13 | - Separate codebase to handle tournaments. 14 | - Started, still single developer 15 | - Any improvements to [gobasic](https://github.com/skx/gobasic) to be pushed back. -------------------------------------------------------------------------------- /docs/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security issues 2 | 3 | Report all security issues with the ``security`` tag. 4 | 5 | -------------------------------------------------------------------------------- /docs/STYLE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # STYLE GUIDE 2 | 3 | I am going to try and distill the multiple guides into one document. Starting with **Effective Go** and then **Ubers Style Guide**. 4 | 5 | ## Editors 6 | 7 | I cannot tell you which editor to use, but I do recommend Microsoft's Visual Studio Code. With the Go extensions, many of the format guides are done automatically. 8 | 9 | ## Code Formatting 10 | 11 | - gofmt will reformat the source to the Go standards and can find many errors in the source code. 12 | 13 | ## Commenting 14 | - blocks of comments should be surrounded by `/*` `*/`, single-line comments should use `//`. 15 | 16 | ## Code Documentation 17 | - Every package should have a package comment, a block comment preceding the package clause. For multi-file packages, the package comment only needs to be present in one file, and anyone will do. The package comment should introduce the package and provide information relevant to the package as a whole. 18 | 19 | ## MixedCaps {#mixed-caps} 20 | 21 | The convention in Go is to use `MixedCaps` or `mixedCaps` rather than underscores to write multiword names. 22 | 23 | ## Interface names 24 | 25 | By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun:`Reader`, `Writer`, `Formatter`, `CloseNotifier` etc. 26 | 27 | There are several such names and it's productive to honor them and the function names they capture. `Read`, `Write`, `Close`, `Flush`, `String`, and so on have canonical signatures and meanings. To avoid confusion, don't give your method one of those names unless it has the same signature and meaning. Conversely, if your type implements a method with the same meaning as a method on a well-known type, give it the same name and signature; call your string-converter method `String` not `ToString`. 28 | 29 | ### Getters {#Getters} 30 | 31 | Go doesn't provide automatic support for getters and setters. There's nothing wrong with providing getters and setters yourself, and it's often appropriate to do so, but it's neither idiomatic nor necessary to put `Get` into the getter's name. If you have a field called `owner` (lower case, unexported), the getter method should be called `Owner` (upper case, exported), not `GetOwner`. The use of upper-case names for export provides the hook to discriminate the field from the method. A setter function, if needed, will likely be called `SetOwner`. Both names read well in practice: 32 | 33 | owner := obj.Owner() 34 | if owner != user { 35 | obj.SetOwner(user) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /docs/UPDATES.md: -------------------------------------------------------------------------------- 1 | # UPDATES 2 | 3 | --- 4 | - 10-13-2021 5 | - Added OSX darwin 6 | - fixed end game detection 7 | - documentation spelling 8 | - 10-05-2021 9 | - Teams is working. Not fully tested yet. 10 | - 09-24-2021 11 | - Updating documentation 12 | - 09-21-2021 13 | - Updated webpages 14 | - New gif 15 | - 09-20-2021 16 | - More commenting 17 | - blaster.bas is the new top bot. 18 | - Better makefile 19 | - 09-13-2021 20 | - More documentation on basicbots.md 21 | - corner-v2.bas is out preforming the other test robots. 22 | - 09-12-2021 23 | - bug #12 fixed 24 | - version update 25 | - 09-11-2021 26 | - bug #1 fixed 27 | - version added 28 | - roadmap added 29 | - 09-10-2021 30 | - Moved from local gitea to github. 31 | - Added CONTRIBUTING.ms, SECURITY.md & UPDATES.md 32 | 33 | -------------------------------------------------------------------------------- /docs/truthtable.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/docs/truthtable.ods -------------------------------------------------------------------------------- /documentation/BASIC-old.md: -------------------------------------------------------------------------------- 1 | # **basicbots** *BASIC* 2 | 3 | **basicbots** version of *BASIC* is based and adapted from [gobasic](https://github.com/skx/gobasic) by [James Kemp](https://github.com/skx). Mr. Kemp has gracely given me permission to adapt [gobasic](https://github.com/skx/gobasic) for my game. 4 | 5 | 6 | ## What is *BASIC* 7 | *BASIC* stands for Beginners' All-purpose Symbolic Instruction Code 8 | 9 | *BASIC* was developed at Dartmouth College in 1964 by John G. Kemeny and Thomas E. Kurtz. It was designed with the philosophy of ease of use. It allowed students in fields other than science and mathematics to use computers. It has a long lasting leagcy spanning mainframes to personal computers and now the IoT. 10 | 11 | --- 12 | 13 | ## **basicbots** version 14 | 15 | **basicbot** uses a subset of the *BASIC* language. Most of the early functionality is present with addition of commands for controlling the robots. 16 | 17 | I encourage you to seek out online guides. 18 | 19 | ### Qurkes 20 | - Variables are case sensitive. Primitives are not. 21 | - LOCALX is different than localx 22 | - Parens around statemenst are not allowed. Example, ```LET A = SQR(25)``` is an error. Where as ```LET A = SQL 25``` is correct. 23 | - Line numbers are not strictly required but are strongly suggested. 24 | - I do not suggest leaving out the line numbers. Error handling is still awful and needs work. Doing this would just make it harder to debug your program. 25 | - Some of the custom statements seem not to play well with `IF` `THEN` & `ELSE`. I am not sure why but for now keep the `IF` states as simple as you can. If you get odd error messages. Rewrite the `IF` states around it. 26 | - Error reporting can be a bit difficult to interpret. 27 | - Lot at the line after the reported error for possible problems. 28 | - Advice, save work often and make copies as you go. **git** is a good way to handle this. 29 | - I find it useful, at the top of the program to initialize all the variables. This cuts down on some errors. 30 | 31 | ### Limitations 32 | A previously stated this is a subset of the *BASIC* standard. 33 | - There is no editor. 34 | - Errors in the program are only found during execution. 35 | - Only a single statement is allowed on each line. The exception is for `REM`arks. `REM`arks can be placed after ':' at the end of a line. 36 | - The only data types are floating-point and strings. 37 | - Arrays can have a maximum of two dimensions. 38 | - `LET` is not optional. 39 | - Only a single statement is allowed between `THEN` and `ELSE` and a single statement from `ELSE` to a newline. 40 | 41 | ### Program format 42 | 43 | Programs are text files that are specified on the command line. They are loaded and parsed when the robots are initialized. 44 | 45 | Each line contains a line number followed by a statement or command with an optional parameter(s). 46 | 47 | All line numbers are integers. 48 | 49 | ### basicbots robot statements 50 | 51 | - `SCAN` direction, width 52 | - Scan for other robots. Returning the range if any are found. 53 | - "direction" is the direction to scan 54 | - "width" is how wide the scan is. Width is specified as +- width. For example, SCAN 90, 10 would scan @ 90 degrees with a width that spans from 80 to 100 degrees. 55 | - `CANNON` direction, range 56 | - Fire the cannon in the direction "direction" with it exploding at the distance specified by "range" 57 | - `SPEED` 58 | - Returns the current speed of the robot. 59 | - `LOCX` 60 | - Returns the current X location of the robot on the battlefield. 61 | - `LOCY` 62 | - Returns the current Y location of the robot on the battlefield. 63 | - `DRIVE` direction, speed 64 | - Starts the drive system in the direction of "direction" with the speed specified. Turns can only be made when the speed is 50 or below. If the speed is above 50, the robot will slow down and then turn, returning to the former speed. 65 | - `DAMAGE` 66 | - Returns the amount of damage the robot has sustained. 67 | 68 | ### *BASIC* primitives 69 | 70 | - Comments 71 | - `REM` Single-line comment only. 72 | 73 | - Variable assignment 74 | - `LET` 75 | - `DIM` 76 | 77 | - Loops 78 | - `FOR` / `NEXT` / `STEP` / `TO` 79 | 80 | - Conditionals 81 | - `IF` / `THEN` / `ELSE` 82 | 83 | - Communication 84 | - `PRINT` & `INPUT` 85 | 86 | - Program termination 87 | - `END` 88 | 89 | - Branching 90 | - `GOTO` & `GOSUB` / `RETURN` 91 | 92 | - Stored data 93 | - `READ` & `DATA` 94 | 95 | - Misc 96 | - `SWAP` 97 | - `DEF FN` & `FN` 98 | 99 | - Strings 100 | - `LEN` 101 | - `LEFT$` & `RIGHT$` 102 | - `CHR$` 103 | - `CODE` 104 | 105 | - Math 106 | - ABS 107 | - ACS 108 | - ATN 109 | - ATN2 110 | - BIN 111 | - COS 112 | - EXP 113 | - INT 114 | - LN 115 | - LOG 116 | - PI 117 | - RND 118 | - SGN 119 | - SIN 120 | - SQR 121 | - TAN 122 | - VAL 123 | 124 | - `LET` variable = expression 125 | - Assigns a value, or the result of expression, to a variable. Variables are case sensitive and can be floating-point or strings. 126 | 127 | - `IF` condition `THEN` statement `ELSE` statement 128 | - If the condition is true, then the statement following `THEN` is executed. If it is false then the statement after `ELSE` is executed. 129 | 130 | - `GOTO` line number 131 | - Transfer execution to another part of the program specified by the line number. 132 | 133 | - `GOSUB` line number 134 | - Transfers execution to a subroutine specified by the line number. The position of the `GOSUB` is remembered so that a `RETURN` can bring program execution back to the statement following the `GOSUB`. 135 | 136 | - `RETURN` 137 | - Returns from a subroutine. Program execution returns to the statement following the `GOSUB`, which called the present subroutine. 138 | 139 | - `END` 140 | - Terminates program execution. 141 | - Instant death to a robot. 142 | 143 | ~~PRINT output-list~~ 144 | 145 | ~~Produces output to the console. Output-list is a list of items separated by commas. Each item can be either a string literal enclosed in double quotation marks or a numeric expression. An end of line sequence is output after all the values so that the next PRINT statement will put its output on a new line.~~ 146 | 147 | ~~INPUT variable-list~~ 148 | 149 | ~~Asks for input from the console. Variable-list is a list of variable names. For each variable given, a question mark is output, and the value typed by the user is stored in that variable. Tinybasic allows multiple values to be typed by the user on one line, each separated by any non-numeric character.~~ 150 | 151 | - `REM` comment-text 152 | - Provides space for free-format comment text in the program. Comments have no effect on the execution of a program and exist only to provide human-readable information to the programmer. 153 | 154 | ### EXPRESSIONS 155 | 156 | Expressions in **basicbots** *BASIC* are purely arithmetic expressions. The four basic arithmetic operators are supported: multiplication (*), division (/), addition (+), subtraction (-) and (%) modulus. Unary operators for positive (+) and negative (-) are supported, as are parentheses for affecting the order of operations. 157 | 158 | Standard operator precedence evaluates parentheses first, then unary signs, then multiplication and division, with addition and subtraction last. 159 | 160 | ### CONDITIONS 161 | 162 | The relational operators are =, >, <, <> or ><, >=, and <=. They are not supported within arithmetic expressions, but can only be used as conditions in IF statements in the form: expression relational-operator expression 163 | 164 | - `IF a < b THEN ..` 165 | - `IF a > b THEN ..` 166 | - `IF a <= b THEN ..` 167 | - `IF a >= b THEN ..` 168 | - `IF a = b THEN ..` 169 | - `IF a <> b THEN ..` 170 | - `IF a THEN ..` 171 | - This passes if `a` is a number which is not zero. 172 | - This passes if `a` is a string which is non-empty. 173 | 174 | That's a lot to take in. In the next section we will break it down by using real examples. 175 | 176 | --- 177 | 178 | ## Intorduction to *BASIC* programming. 179 | 180 | 181 | 182 | ### Assignment 183 | `LET` assignes a number or result to a variable. `LET` will be used often in most programs. 184 | 185 | - `10 LET A = 0` 186 | - Would assign the number 0 to the variable named A. 187 | - `10 LET A = A + 1` 188 | - Would increment the variable by adding 1 to A and storing the results back in A. 189 | - `10 LET DISTANCE = SCAN A, 5` 190 | - The results of a `SCAN` at angle `A` with a width of 5 would be stored in the variable `DISTANCE' 191 | 192 | ### Conditionals 193 | - `>` more than. 194 | - `IF A > B THEN LET A = A + 10` 195 | - if `A` is more than `B` then add `10` to the variable `A` 196 | - `<` lets than 197 | - `IF A < B THEN LET A = A - 10` 198 | - if `A` is less than `B` then subtract `10` from the variable `A` 199 | - `=` equals 200 | - `IF A = B THEN GOTO 100 ELSE GOTO 200` 201 | - if `A` is equal to `B` then jump to location `100` else jump to location `200` 202 | - `>=` more than or equals 203 | - `IF A >= B THEN LET A = A + 10` 204 | - if `A` is more than or equals `B` then add `10` to the variable `A` 205 | - `<=` lets than or equals 206 | - `IF A <= B THEN LET A = A - 10` 207 | - if `A` is less than or equals `B` then subtract `10` from the variable `A` 208 | - `<>` does not equal 209 | - `IF A <> B THEN GOSUB 200` 210 | - If `A` does not equal B then `GOSUB` to location 200 211 | - no conditional 212 | - `IF A THEN LET B = A` 213 | - If `A` is not 0 then assign the value in `A` to the variable `B` 214 | - If `A` is any number expect 0 then ... 215 | 216 | ## Loops 217 | 218 | Loops allow an effecient way of doing a section of code over a set number of times. 219 | 220 | 5 LET B = 0 221 | 10 FOR A = 1 TO 10 222 | 20 LET B = B + 1 223 | 25 PRINT B, 224 | 30 NEXT A 225 | 35 PRINT "\n" 226 | 40 END 227 | 228 | 229 | Will output 230 | 231 | 1 2 3 4 5 6 7 8 9 10 232 | 233 | You can control the step that each iteration does, 234 | 235 | 10 FOR A = 1 TO 10 STEP 2 236 | 20 PRINT A, 237 | 30 NEXT A 238 | 35 PRINT "\n" 239 | 40 END 240 | 241 | Will output 242 | 243 | 1 3 5 7 9 244 | 245 | Not wha you expected? We started the loop at 1, so 1 + 2 = 3. You can change this by starting your loop at 0. 246 | 247 | 10 FOR A = 0 TO 10 STEP 2 248 | 20 PRINT A, 249 | 30 NEXT A 250 | 35 PRINT "\n" 251 | 40 END 252 | 253 | 254 | will output 255 | 256 | 0 2 4 6 8 10 257 | 258 | Loops can also step by a fraction. 259 | 260 | 10 FOR A = 0 TO 1 STEP 0.2 261 | 20 PRINT A, 262 | 30 NEXT A 263 | 35 PRINT "\n" 264 | 40 END 265 | 266 | will output 267 | 268 | 0 0.200000 0.400000 0.600000 0.800000 1 269 | 270 | ## Branching 271 | 272 | `GOTO` will jump to the specified line number. 273 | 274 | 10 PRINT "Hello World\n" 275 | 20 GOTO 10 276 | 277 | This will print *Hello World* over and over again 278 | 279 | `GOSUB` will jump to a subroutine then will `RETURN` back to the next like after the `GOSUB`. This is usefull in making sections of code to be only written once. 280 | 281 | 10 PRINT "Start\n" 282 | 20 FOR A = 1 TO 10 283 | 30 GOSUB 100 284 | 40 NEXT A 285 | 50 PRINT "End\n" 286 | 60 END 287 | 100 PRINT "I am a subroutine!\n" 288 | 110 RETURN 289 | 290 | will output 291 | 292 | Start 293 | I am a subroutine! 294 | I am a subroutine! 295 | I am a subroutine! 296 | I am a subroutine! 297 | I am a subroutine! 298 | I am a subroutine! 299 | I am a subroutine! 300 | I am a subroutine! 301 | I am a subroutine! 302 | I am a subroutine! 303 | End 304 | 305 | 306 | --- 307 | 308 | # Programming a robot 309 | 310 | ## Example 1 - sniper-v1.bas 311 | 312 | 1 REM FIND TARGET AND SHOOT 313 | 10 LET SCANDIR = 0 314 | 12 LET SCANWIDTH = 5 315 | 30 LET D = SCAN SCANDIR, SCANWIDTH 316 | 40 IF D >= 50 AND D <= 700 THEN GOSUB 210 317 | 50 LET SCANDIR = SCANDIR + 2 318 | 55 IF SCANDIR > 360 THEN LET SCANDIR = SCANDIR - 360 319 | 56 IF SCANDIR < 0 THEN LET SCANDIR = SCANDIR + 360 320 | 60 GOTO 30 321 | 322 | 200 REM FIRE CANNON 323 | 210 CANNON SCANDIR,D 324 | 220 RETURN 325 | 326 | - Line 10,12 initalize the scan direction and scan width. 327 | - Line 30 scans in the direction of SCANDIR with a width of SCANWIDTH 328 | - Line 40 if the distance returned by scan is more than 50 (dont want to hurt ourselves) and less than 700 then jump to subroutine location at - line 210 329 | - line 50 adds 2 to the scan direction. 330 | - line 55 if scan direction more than 360 then subtract 360. 331 | - line 56 if scan direction is less than 0 then add 360. 332 | - The previous line were to make sure the scan direction stayed between 0 and 360. 333 | - line 60 jumps back line 30 and starts the scan again 334 | - line 210 fires the cannon at scan direction and the distance returned by results from line 30. 335 | - line 220 returns back to line after the GOSUB. In this case it would be line 50. 336 | 337 | 338 | 339 | ## Lets get started with *basicbots* programming. 340 | 341 | Actual working code! Featuring a subroutine. 342 | 343 | ``` 344 | 1 REM scan and shoot 345 | 10 LET DISTANCE = 0 346 | 11 LET ANGLE = 0 347 | 12 LET WIDTH = 5 348 | 349 | 100 GOSUB 1000 350 | 110 IF DISTANCE < 700 THEN CANNON ANGLE,DISTANCE 351 | 120 GOTO 100 352 | 353 | 1000 REM scan for enemy robot. 354 | 1010 LET DISTANCE = SCAN ANGLE, WIDTH 355 | 1020 IF DISTANCE > 0 THEN GOTO 1100 356 | 1030 LET ANGLE = ANGLE + WIDTH 357 | 1040 IF ANGLE >= 360 THEN LET ANGLE = ANGLE - 360 358 | 1050 GOTO 1010 359 | 1100 RETURN 360 | 361 | ``` 362 | 363 | 364 | 365 | The flow is 366 | - initialize all variables 367 | - jump to subroutine 368 | - scan for enemy robot 369 | - if found return frome subroutine 370 | - if not found add the width size to the angle 371 | - jump back to scan command 372 | - return from subroutine 373 | - if the distance to the enemy is less than 700 then shot cannon at the angle of the last scan with the distance returned 374 | - do it all again 375 | 376 | 377 | 378 | 379 | 380 | --- 381 | So much more to do. 382 | 383 | --- 384 | --- 385 | 386 | ## Interesting articles and Guides on *BASIC* 387 | 388 | [Fifty Years of *BASIC*, the Programming Language That Made Computers Personal](https://web.archive.org/web/20150708131957/http://time.com/hive.org/web/20150708131957/http://time.com/69316/basic/) 389 | 390 | [*BASIC* Programming](https://en.wikibooks.org/wiki/BASIC_Programming) 391 | 392 | [Begginers Guide to *BASIC*](http://www.hoist-point.com/applesoft_basic_tutorial.htm) 393 | 394 | -------------------------------------------------------------------------------- /documentation/BASIC.md: -------------------------------------------------------------------------------- 1 | # Ugly notes 2 | 3 | Math 4 | ``` 5 | ABS ABS returns the absolute value of a number, which is the number without its sign 6 | ACS ACS (arccosine) 7 | ASN ASN (arcsine) 8 | ATN ATN (arctan) 9 | ATN2 ATN2 returns the arc tangent of y/x, using the signs of the two to determine the quadrant of the return value 10 | BIN BIN converts a number from binary. 11 | COS COS implements the COS function.. 12 | EXP EXP x=e^x EXP 13 | INT INT implements INT 14 | LN LN calculates logarithms to the base e - LN 15 | LOG LN calculates logarithms to the base e - LN 16 | PI PI returns the value of PI 17 | RND RND implements RND 18 | SGN SGN is the sign function (sometimes called signum). 19 | SIN SIN operats the sin function. 20 | SQR SQR implements square root. Modified to not error on 0 21 | TAN TAN implements the tan function. 22 | VAL VAL converts a string to a number 23 | ``` 24 | 25 | Strings 26 | ``` 27 | CHR$ CHR returns the character specified by the given ASCII code. 28 | CODE CODE returns the integer value of the specified character. 29 | LEFT$ LEFT returns the N left-most characters of the string. 30 | LEN LEN returns the length of the given string 31 | MID$ MID returns the N characters from the given offset 32 | RIGHT$ RIGHT returns the N right-most characters of the string. 33 | SPC SPC returns a string containing the given number of spaces 34 | STR$ STR converts a number to a string 35 | TL$ TL returns a string, minus the first character. 36 | STRC$ Builds a string converting a number to a x number of digits. 37 | ``` 38 | 39 | Robots 40 | ``` 41 | LOCX 42 | LOCY 43 | SPEED 44 | DAMAGE 45 | DRIVE 46 | SCAN 47 | CANNON 48 | IN - Get data from another robot. 49 | OUT - Send data to another robot. 50 | TEAM - Turn teams on for this robot. Returns the team number. 51 | ``` -------------------------------------------------------------------------------- /documentation/BASICBOTS.md: -------------------------------------------------------------------------------- 1 | # basicbots overview and guide 2 | 3 | This is the overview manual for **basicbots** programming game; with examples that will hopefully start you on your way. 4 | 5 | --- 6 | 7 | ## The simulation 8 | 9 | ### The Battlefield 10 | The robots compete in a virtual battlefield that is 1000 units wide by 1000 units long. The upper left corner is 0,0 while the lower right corner is 1000,1000. There is a virtual wall that surrounds the battlefield. If a robot makes contact with this wall, it will take damage and its speed will be reduced to 0. 11 | 12 | ### Cardinal directions 13 | | | | | 14 | |:----|:---:| ---:| 15 | | 225 | 270 | 315 | 16 | | 180 | * | 000 | 17 | | 135 | 090 | 045 | 18 | | | | | 19 | 20 | 21 | ### The Robot's Hardware 22 | The robot tank has three hardware systems. 23 | 24 | - **A Scanner**. The scanner can scan in any direction instantly but has an angular resolution from +-2 degrees to +- 10 degrees. Example: Scan 90 degrees with a width of 2 would return results that are between 88 - 92 degrees. A width of 10 would return results that are from 80 to 100 degrees. 25 | 26 | - **A Cannon**. The Cannon can fire a projectile in any direction but at a maximum range of 700 units. Any robot caught in the blast radius of 40 units. The farther from the center of the blast, the less damage a robot will take. Each robot can only have two projectiles in the air at any given time and it takes time to reload. 27 | 28 | - **A Drive system**. The drive system has two parameters. Angle and speed. The angle can be any angle from 0 to 360 degrees and the speed can be from 0 to 100%. The robots do require time to reach a given speed and require time to slow down. The robot can only negotiate turns when the speed is less than 50%. If the speed is above 50% the robot will slow down to 50% then turn and increase speed to the previous setting. 29 | 30 | ### The Robot's Status 31 | The robot has access to: 32 | - **The X location**. The current X position on the battlefield of the robot. 33 | - **The Y location**. The current Y position on the battlefield of the robot. 34 | - **The Speed**. The current speed of the robot. 35 | - **The Damage**. The current amount of damage inflicted upon the robot. The robot can withstand up to 100 points of damage. Once a robot has reached 100 points, it has died and is removed from the battlefield. The amount of damage does not affect its performance. 36 | 37 | That's it. Three commands and four status sensors. With that, you can navigate the battlefield and annihilate the other robots! 38 | 39 | 40 | 41 | # The *BASIC* of **basicbots** 42 | 43 | Keywords 44 | 45 | | Command | | | | | | Notes | 46 | |:--------|:-----|:-----|:-|:-|:-| :------| 47 | | REM | Single line | | | | | A remark | 48 | | LET | Variable| = Value | | | | Assign a value to a variable | 49 | | GOTO | Line number| | | | | Transfer execution to the line number | 50 | | GOSUB | Line number| | | | | Transfer execution to the subroutine at line number | 51 | | RETURN | | | | | | Transfers execution to the line following the GOSUB command| 52 | | IF | condition | THEN | statement | ELSE | statement | Excute the statement after THEN if condition is true if not then execute statement after ELSE 53 | | FOR | assignment | TO | value | STEP | value | Set the loop paramters 54 | | NEXT | variable | | | | | Return back to FOR changing the value of the assignment by the value in STEP | 55 | | DIM | variable | [size] | | | | Create an array of the size specified. Note this array is 0 based unlike other BASICs.| 56 | | DEF | FN | name | expresstion | | | Create a user defined function | 57 | | FN | name | values | | | | execute defined funtion name | 58 | | SWAP | variable1 | variable2| | | | Exchange the values variable1 and variable2 | 59 | | DATA | values, | | | | | List of values to be read | 60 | | READ | | | | | | Reads the data stored by DATA. There is no reset ability at this time.| 61 | | AND | value1 | value2 | | | | Bit AND of the value1 and value2. See [truth table](#truth-table) | 62 | | OR | value1 | value2 | | | | Bit OR of the value1 and value2. See [truth table](#truth-table) | 63 | | XOR | value1 | value2 | | | | Bit XOR or the value1 and value2. See [truth table](#truth-table) | 64 | | LOCX | | | | | | Returns the X Location of the robot.| 65 | | LOCY | | | | | | Returns the Y Location of the robot.| 66 | | DAMAGE | | | | | | Returns the current damage of the robot.| 67 | | SPEED | | | | | | Returns the current speed of the robot.| 68 | | SCAN | angle| width| | | | Scan for an enemy robot in the direction of angle with a width of +- width. 69 | | CANNON | angle | range | | | | Fire the cannon at angle and range | 70 | | DRIVE | angle | speed% | | | | Engage the drive in the direction of angle with a speed of speed% 71 | 72 | 73 | --- 74 | 75 | ### Operators 76 | 77 | |Operator | | 78 | |:------ | :--- | 79 | | = | Assign | 80 | | * | Multiplication | 81 | | - | Subtraction | 82 | | % | Modulus | 83 | | + | Addition | 84 | | / | Division | 85 | | ^ | Power | 86 | | ( | Left Paren | 87 | | ) | Right Paren | 88 | | [ | Left index bracket | 89 | | ] | Right index bracket | 90 | | | | 91 | 92 | 93 | |Operator | Returns non zero if true | 94 | |:------ | :--- | 95 | | > | Greater than | 96 | | >= | Greater than or equal to | 97 | | < | Less than | 98 | | <= | Less than or equal to | 99 | | <> | Not equal to | 100 | 101 | 102 | 103 | 104 | 105 | 106 | --- 107 | 108 | ### Truth table 109 | 110 | | | VALUE | VALUE | RESULT | 111 | |:----|:------|:------|:-------| 112 | | AND | 0 | 0 | 0 | 113 | | AND | 1 | 0 | 0 | 114 | | AND | 0 | 1 | 0 | 115 | | AND | 1 | 1 | 1 | 116 | 117 | 48 AND 12 = 0 118 | 0000000000110000 = 48 119 | 0000000000001100 = 12 120 | 0000000000000000 = 0 121 | 122 | 48 AND 16 = 16 123 | 0000000000110000 = 48 124 | 0000000000010000 = 16 125 | 0000000000010000 = 16 126 | 127 | | | VALUE | VALUE | RESULT | 128 | |:----|:------|:------|:-------| 129 | | OR | 0 | 0 | 0 | 130 | | OR | 1 | 0 | 1 | 131 | | OR | 0 | 1 | 1 | 132 | | OR | 1 | 1 | 1 | 133 | 134 | 48 OR 12 = 60 135 | 0000000000110000 = 48 136 | 0000000000001100 = 12 137 | 0000000000111100 = 60 138 | 139 | 48 OR 16 = 48 140 | 0000000000110000 = 48 141 | 0000000000010000 = 16 142 | 0000000000110000 = 48 143 | 144 | | | VALUE | VALUE | RESULT | 145 | |:----|:------|:------|:-------| 146 | | XOR | 0 | 0 | 0 | 147 | | XOR | 1 | 0 | 1 | 148 | | XOR | 0 | 1 | 1 | 149 | | XOR | 1 | 1 | 0 | 150 | 151 | 48 XOR 12 = 60 152 | 0000000000110000 = 48 153 | 0000000000001100 = 12 154 | 0000000000111100 = 60 155 | 156 | 48 XOR 16 = 32 157 | 0000000000110000 = 48 158 | 0000000000010000 = 16 159 | 0000000000100000 = 32 160 | 161 | ## What's next? Example code? 162 | 163 | Some of the following code is tested on the original gobasic. It applies here as well. 164 | 165 | 166 | ## Hello World 167 | Let's start with the universal first program. helloworld.bas 168 | ``` 169 | 10 PRINT "Hello World\n" 170 | 20 END 171 | ``` 172 | ``` 173 | ~/go/src/gobasic$ ./gobasic helloworld.bas 174 | Hello World 175 | ~/go/src/gobasic$ 176 | ``` 177 | 178 | If you followed along congratulations, you just wrote your first basic program. 179 | 180 | ## `GOTO` 181 | 182 | Now let's introduce a `GOTO`. Remember `GOTO` transfers execution to another line in the program. 183 | 184 | ``` 185 | 10 PRINT "Hello World\n" 186 | 20 GOTO 10 187 | ``` 188 | Notice the lack of the `END` statement. That's because this program never ends. After printing Hello World on line 10, the `GOTO` on line 20 jumps back to line 10 and it starts all over again. 189 | 190 | ``` 191 | Hello World 192 | Hello World 193 | Hello World 194 | ... 195 | ... 196 | --- 197 | Hello World 198 | Hello World 199 | Hello World 200 | ``` 201 | That is a bit much. Just printing Hello World over and over, forever. 202 | 203 | ## `FOR` `TO` `NEXT` `STEP` 204 | 205 | Let's set up a `FOR` `NEXT` loop. 206 | 207 | ``` 208 | 10 FOR I = 1 TO 5 209 | 20 PRINT "Hello World\n" 210 | 30 NEXT I 211 | 40 END 212 | ``` 213 | 214 | ``` 215 | ~/go/src/gobasic$ ./gobasic helloworld.bas 216 | Hello World 217 | Hello World 218 | Hello World 219 | Hello World 220 | Hello World 221 | ~/go/src/gobasic$ 222 | ``` 223 | 224 | The `FOR` on line 10 assigns the variable I the value of 1 and sets the limit to 5. This is the start of the loop. 225 | The lines following the `FOR` will execute until the loop is finished. 226 | The `NEXT I` causes the execution to transfer back to line 10 where I will be incremented by 1 and start the loop again. Once I equals 5 the `NEXT` statement will transfer execution to the next line. 227 | In this case, will be the `END` and the program will terminate. 228 | 229 | Let's show that it is incrementing. We will modify the `PRINT` state to include variable I. 230 | 231 | ``` 232 | 10 FOR I = 1 TO 5 233 | 20 PRINT "Hello World ",I,"\n" 234 | 30 NEXT I 235 | 40 END 236 | ``` 237 | 238 | ``` 239 | ~/go/src/gobasic$ ./gobasic helloworld.bas 240 | Hello World 1 241 | Hello World 2 242 | Hello World 3 243 | Hello World 4 244 | Hello World 5 245 | ~/go/src/gobasic$ 246 | ``` 247 | 248 | It is easy to see that the variable I is incrementing by 1 on each pass of the loop. Next, we will check the `STEP` size. 249 | 250 | ``` 251 | 10 FOR I = 1 TO 10 STEP 2 252 | 20 PRINT "Hello World ",I,"\n" 253 | 30 NEXT I 254 | 40 END 255 | ``` 256 | 257 | ``` 258 | ~/go/src/gobasic$ ./gobasic helloworld.bas 259 | Hello World 1 260 | Hello World 3 261 | Hello World 5 262 | Hello World 7 263 | Hello World 9 264 | ~/go/src/gobasic$ 265 | 266 | ``` 267 | You can see that the loop stepped by 2 on each pass of the loop. 268 | Notice that the number 10 is never shown. That's because I was larger than 10 so the execution went to the line following `NEXT`. 269 | You can all step backward if needed. 270 | 271 | ``` 272 | 10 FOR I = 5 TO 1 STEP -1 273 | 20 PRINT "Hello World ",I,"\n" 274 | 30 NEXT I 275 | 40 END 276 | ``` 277 | ``` 278 | ~/go/src/gobasic$ ./gobasic helloworld.bas 279 | Hello World 5 280 | Hello World 4 281 | Hello World 3 282 | Hello World 2 283 | Hello World 1 284 | ~/go/src/gobasic$ 285 | 286 | ``` 287 | 288 | ## `LET` 289 | 290 | `LET` sets the listed value to equal the number or statement 291 | 292 | Simple assignment. 293 | 294 | ``` 295 | 10 LET A = 1 296 | 20 PRINT A,"\n" 297 | 30 END 298 | ``` 299 | ``` 300 | ~/go/src/gobasic$ ./gobasic variables.bas 301 | 1 302 | ~/go/src/gobasic$ 303 | ``` 304 | 305 | Variable A has the value of 1. With `LET` more complete variable assignments can be done. 306 | 307 | ``` 308 | 10 LET A = 1 309 | 15 LET B = 2 310 | 20 LET A = A + B 311 | 25 LET C = B - 1 312 | 20 PRINT A,B,C,"\n" 313 | 30 END 314 | ``` 315 | 316 | Hey, what was all that? Think about it before looking at the results below. Did it do what you thought it would? 317 | 318 | ``` 319 | ~/go/src/gobasic$ ./gobasic variables.bas 320 | 3 2 1 321 | ~/go/src/gobasic$ 322 | ``` 323 | 324 | Next, we will use a math function in the assignment. 325 | 326 | ``` 327 | 5 PRINT "Pythagorean Theorem\n" 328 | 10 LET A = 3 329 | 20 LET B = 4 330 | 30 LET C = SQR A + SQR B 331 | 40 LET C = C * C 332 | 40 PRINT A,B,C,"\n" 333 | 50 END 334 | ``` 335 | ``` 336 | ~/go/src/gobasic$ ./gobasic variables.bas 337 | Pythagorean Theorem 338 | 3 4 5.000000 339 | ~/go/src/gobasic$ 340 | ``` 341 | 342 | -------------------------------------------------------------------------------- /documentation/CODE-EXAMPLES.md: -------------------------------------------------------------------------------- 1 | # **basicbots** code library 2 | 3 | A curated list of subroutines to help people get started. 4 | 5 | 6 | ## Get the angle to a target 7 | - Variables passed 8 | - DX = target X 9 | - DY = target Y 10 | - Variables returned 11 | - ANTT = Angle to target 12 | ```basic 13 | 9000 LET LLX = LOCX 14 | 9005 LET LLY = LOCY 15 | 9010 LET ANTT = ATN2 DY - LLY, DX - LLX 16 | 9015 RETURN 17 | ``` 18 | 19 | ## Get the distance to target 20 | - Variables passed 21 | - DX = target X 22 | - DY = target Y 23 | - Returned variables 24 | - DTT = Distance to target 25 | ```basic 26 | 9050 LET LX = LOCX 27 | 9055 LET LY = LOCY 28 | 9060 LET TX = DX - LX 29 | 9065 LET TY = DY - LY 30 | 9070 LET DTT = SQR (TX*TX) + (TY*TY) 31 | 9075 RETURN 32 | ``` 33 | 34 | ## Pick random location in the battlefield. Staying safely away from the walls. 35 | - Variables passed 36 | - None 37 | - Returned variables 38 | - DX = Destination X 39 | - DY = Destination Y 40 | ```basic 41 | 9100 LET DX = (RND 700) + 100 42 | 9105 LET DY = (RND 700) + 100 43 | 9110 RETURN 44 | ``` 45 | 46 | ## Pick a random location on the battlefield and move towards it at full speed, stopping when close. 47 | 48 | ```basic 49 | 9200 GOSUB 9100 50 | 9210 GOSUB 9000 51 | 9220 DRIVE ANTT, 100 52 | 9230 GOSUB 9050 53 | 9240 IF DTT <= 50 THEN GOTO 9260 54 | 9250 GOTO 9230 55 | 9260 DRIVE ANTT,0 56 | 9270 RETURN 57 | ``` 58 | 59 | ## Pick a random location on the battlefield and move towards it at full speed. Scan and shoot while moving. 60 | - Variables passed 61 | - SCA = Starting scan angle 62 | - SCW = Scanning width 63 | - Returned variables 64 | - None 65 | 66 | ```basic 67 | 9300 GOSUB 9100 68 | 9310 GOSUB 9000 69 | 9320 DRIVE ANTT, 100 70 | 9340 GOSUB 9900 71 | 9350 GOSUB 9050 72 | 9360 IF DTT <= 50 THEN GOTO 9380 73 | 9370 GOTO 9340 74 | 9380 DRIVE ANTT, 0 75 | 9390 RETURN 76 | ``` 77 | 78 | ## Simple scan and shoot. Keep scan angle between calls. 79 | - Variables passed 80 | - SCA = Starting scan angle 81 | - SCW = Scanning width 82 | - Returned variables 83 | - SCA = New starting scan angle 84 | 85 | ```basic 86 | 9900 LET SDTT = SCAN SCA,SCW 87 | 9905 IF SDTT > 0 AND SDTT < 700 THEN GOTO 9920 88 | 9910 LET SCA = SCA + SCW 89 | 9915 RETURN 90 | 9920 CANNON SCA,SDTT 91 | 9930 GOTO 9900 92 | ``` 93 | 94 | ## Try to lead the shot. Call after first scan. 95 | - Variables passed 96 | - SCA = Scan angle 97 | - SCW = Scan width 98 | - Returned variables 99 | - none 100 | 101 | ```basic 102 | 9970 LET DTTN = SCAN SCA,SCW 103 | 9975 LET DIF = DTTN + (DTTN - SDTT) 104 | 9980 CANNON RSA, DIF 105 | 9985 RETURN 106 | ``` 107 | -------------------------------------------------------------------------------- /endgame.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // truth table 5 | // -- 3 2 1 0 6 | // 00 0 0 0 0 7 | // 01 0 0 0 1 8 | // 02 0 0 1 0 9 | // 03 0 0 1 1 10 | // 04 0 1 0 0 11 | // 05 0 1 0 1 12 | // 06 0 1 1 0 13 | // 07 0 1 1 1 14 | // 08 1 0 0 0 15 | // 09 1 0 0 1 16 | // 10 1 0 1 0 17 | // 11 1 0 1 1 18 | // 12 1 1 0 0 19 | // 13 1 1 0 1 20 | // 14 1 1 1 0 21 | // 15 1 1 1 1 22 | 23 | func endGame() { 24 | nb, a, _ := robotStatusToint() 25 | nbf := float64(nb) 26 | if teams { 27 | if a == 0 { // no survivors - no points 28 | // 29 | } 30 | if a == 1 { // Robots[0] is the winner, Robot[1] gets win as well. 31 | Robots[0].Winner += 1 32 | Robots[0].Points += nbf 33 | Robots[1].Winner += 1 34 | Robots[1].Points += nbf 35 | Robots[2].Lose += 1 36 | Robots[3].Lose += 1 37 | } 38 | if a == 2 { // Robots[0] is the winner. Robots[1] gets win as well. 39 | Robots[0].Winner += 1 40 | Robots[0].Points += nbf 41 | Robots[1].Winner += 1 42 | Robots[1].Points += nbf 43 | Robots[2].Lose += 1 44 | Robots[3].Lose += 1 45 | } 46 | if a == 3 { // Robots[0] and Robots[1] are the winners 47 | Robots[0].Winner += 1 48 | Robots[0].Points += nbf 49 | Robots[1].Winner += 1 50 | Robots[1].Points += nbf 51 | Robots[2].Lose += 1 52 | Robots[3].Lose += 1 53 | } 54 | if a == 4 { // Robots[2] is the winner and Robots[3] get carried along as well. 55 | Robots[0].Lose += 1 56 | Robots[1].Lose += 1 57 | Robots[2].Winner += 1 58 | Robots[2].Points += nbf 59 | Robots[3].Winner += 1 60 | Robots[3].Points += nbf 61 | } 62 | 63 | if a == 8 { // Robots[3] is the winner and Robots[2] get carried along as well. 64 | Robots[0].Lose += 1 65 | Robots[1].Lose += 1 66 | Robots[2].Winner += 1 67 | Robots[2].Points += nbf 68 | Robots[3].Winner += 1 69 | Robots[3].Points += nbf 70 | } 71 | if a == 12 { // Robots[2] and Robots[3] are the Winners 72 | Robots[0].Lose += 1 73 | Robots[1].Lose += 1 74 | Robots[2].Winner += 1 75 | Robots[2].Points += nbf 76 | Robots[3].Winner += 1 77 | Robots[3].Points += nbf 78 | } 79 | if a == 5 || a == 6 || a == 7 || a == 9 || a == 10 || 80 | a == 11 || a == 13 || a == 14 || a == 15 { // All robots are still alive, must be max cycles. Tie for everyone 81 | Robots[0].Tie += 1 82 | Robots[0].Points += 1 83 | Robots[1].Tie += 1 84 | Robots[1].Points += 1 85 | Robots[2].Tie += 1 86 | Robots[2].Points += 1 87 | Robots[3].Tie += 1 88 | Robots[3].Points += 1 89 | } 90 | } else { 91 | if a == 0 { // Every Robot is dead 92 | Robots[0].Lose += 1 93 | Robots[1].Lose += 1 94 | Robots[2].Lose += 1 95 | Robots[3].Lose += 1 96 | } 97 | if a == 1 { // Robots[0] is the winner 98 | Robots[0].Winner += 1 99 | Robots[0].Points += nbf 100 | Robots[1].Lose += 1 101 | Robots[2].Lose += 1 102 | Robots[3].Lose += 1 103 | } 104 | if a == 2 { // Robots[1] is the winner 105 | Robots[0].Lose += 1 106 | Robots[1].Winner += 1 107 | Robots[1].Points += nbf 108 | Robots[2].Lose += 1 109 | Robots[3].Lose += 1 110 | } 111 | if a == 3 { // 1 & 0 winner 112 | Robots[0].Tie += 1 113 | Robots[0].Points += nbf / 2.0 114 | Robots[1].Tie += 1 115 | Robots[1].Points += nbf / 2.0 116 | Robots[2].Lose += 1 117 | Robots[3].Lose += 1 118 | } 119 | if a == 4 { // Robots[2] is the winner 120 | Robots[0].Lose += 1 121 | Robots[1].Lose += 1 122 | Robots[2].Winner += 1 123 | Robots[2].Points += nbf 124 | Robots[3].Lose += 1 125 | } 126 | if a == 5 { // 0 & 2 winner 127 | Robots[0].Tie += 1 128 | Robots[0].Points += nbf / 2.0 129 | Robots[1].Lose += 1 130 | Robots[2].Tie += 1 131 | Robots[2].Points += nbf / 2.0 132 | Robots[3].Lose += 1 133 | } 134 | if a == 6 { // 1 & 2 winner 135 | Robots[0].Lose += 1 136 | Robots[1].Tie += 1 137 | Robots[1].Points += nbf / 2.0 138 | Robots[2].Tie += 1 139 | Robots[2].Points += nbf / 2.0 140 | Robots[3].Lose += 1 141 | } 142 | if a == 7 { // 0 & 1 & 2 winner 143 | Robots[0].Tie += 1 144 | Robots[0].Points += nbf / 3.0 145 | Robots[1].Tie += 1 146 | Robots[1].Points += nbf / 3.0 147 | Robots[2].Tie += 1 148 | Robots[2].Points += nbf / 3.0 149 | Robots[3].Lose += 1 150 | } 151 | if a == 8 { // Robots[3] is the winner 152 | Robots[0].Lose += 1 153 | Robots[1].Lose += 1 154 | Robots[2].Lose += 1 155 | Robots[3].Winner += 1 156 | Robots[3].Points += nbf 157 | } 158 | if a == 9 { // 0 & 3 winner 159 | Robots[0].Tie += 1 160 | Robots[0].Points += nbf / 2.0 161 | Robots[1].Lose += 1 162 | Robots[2].Lose += 1 163 | Robots[3].Tie += 1 164 | Robots[3].Points += nbf / 2.0 165 | } 166 | if a == 10 { // 1,3 167 | Robots[0].Lose += 1 168 | Robots[1].Tie += 1 169 | Robots[1].Points += nbf / 2.0 170 | Robots[2].Lose += 1 171 | Robots[3].Tie += 1 172 | Robots[3].Points += nbf / 2.0 173 | } 174 | if a == 11 { // 0,1,3 175 | Robots[0].Tie += 1 176 | Robots[0].Points += nbf / 3.0 177 | Robots[1].Tie += 1 178 | Robots[1].Points += nbf / 3.0 179 | Robots[2].Lose += 1 180 | Robots[3].Tie += 1 181 | Robots[3].Points += nbf / 3.0 182 | } 183 | if a == 12 { // 2,3 184 | Robots[0].Lose += 1 185 | Robots[1].Lose += 1 186 | Robots[2].Tie += 1 187 | Robots[2].Points += nbf / 2.0 188 | Robots[3].Tie += 1 189 | Robots[3].Points += nbf / 2.0 190 | } 191 | if a == 13 { // 0,2,3 192 | Robots[0].Tie += 1 193 | Robots[0].Points += nbf / 3.0 194 | Robots[1].Lose += 1 195 | Robots[2].Tie += 1 196 | Robots[2].Points += nbf / 3.0 197 | Robots[3].Tie += 1 198 | Robots[3].Points += nbf / 3.0 199 | } 200 | if a == 14 { // 1,2,3 201 | Robots[0].Lose += 1 202 | Robots[1].Tie += 1 203 | Robots[1].Points += nbf / 3.0 204 | Robots[2].Tie += 1 205 | Robots[2].Points += nbf / 3.0 206 | Robots[3].Tie += 1 207 | Robots[3].Points += nbf / 3.0 208 | } 209 | if a == 15 { // All robots still alive - tie for all. 210 | Robots[0].Tie += 1 211 | Robots[0].Points += 1 212 | Robots[1].Tie += 1 213 | Robots[1].Points += 1 214 | Robots[2].Tie += 1 215 | Robots[2].Points += 1 216 | Robots[3].Tie += 1 217 | Robots[3].Points += 1 218 | } 219 | } 220 | } 221 | 222 | // checkAlive : The one and only test to see if a robot is dead and sets status to dead. 223 | func checkAlive(n int) { 224 | if Robots[n].Status == DEAD { 225 | return 226 | } 227 | if Robots[n].Damage >= 100 { 228 | Robots[n].Status = DEAD 229 | } 230 | 231 | } 232 | 233 | // Convert the status of the robots to an int. 234 | func robotStatusToint() (int, int, int) { 235 | var a int 236 | var d int 237 | 238 | for i := 0; i < numberOfRobots; i++ { 239 | if Robots[i].Status == ALIVE { 240 | a = a | (1 << i) 241 | } else { 242 | d = d | (1 << i) 243 | } 244 | } 245 | 246 | // switch numberOfRobots { 247 | 248 | // case 4: 249 | // if Robots[0].Status == ALIVE { 250 | // a = a | 0x01 251 | // }else{ 252 | // d = d | 0x01 253 | // } 254 | // if Robots[1].Status == ALIVE { 255 | // a = a | 0x02 256 | // }else{ 257 | // d = d | 0x02 258 | // } 259 | // if Robots[2].Status == ALIVE { 260 | // a = a | 0x04 261 | // }else{ 262 | // d = d | 0x04 263 | // } 264 | // if Robots[3].Status == ALIVE { 265 | // a = a | 0x08 266 | // }else{ 267 | // d = d | 0x08 268 | // } 269 | // case 3: 270 | // if Robots[0].Status == ALIVE { 271 | // a = a | 0x01 272 | // } 273 | // if Robots[1].Status == ALIVE { 274 | // a = a | 0x02 275 | // } 276 | // if Robots[2].Status == ALIVE { 277 | // a = a | 0x04 278 | // } 279 | // case 2: 280 | // if Robots[0].Status == ALIVE { 281 | // a = a | 0x01 282 | // } 283 | // if Robots[1].Status == ALIVE { 284 | // a = a | 0x02 285 | // } 286 | // } 287 | 288 | return numberOfRobots, a, d 289 | } 290 | 291 | // endCondition : Check if its end game 292 | // Last update, I totally screws this up. 293 | func endCondition() bool { 294 | 295 | _, a, _ := robotStatusToint() 296 | //fmt.Println(a) 297 | if teams { 298 | if a == 0 || a == 1 || a == 2 || a == 3 || a == 4 || a == 8 || a == 12 { 299 | return true 300 | } else { 301 | return false 302 | } 303 | } else { 304 | if a == 0 || a == 1 || a == 2 || a == 4 || a == 8 { 305 | return true 306 | } else { 307 | return false 308 | } 309 | } 310 | 311 | //return false 312 | 313 | } 314 | -------------------------------------------------------------------------------- /eval/COPYRIGHT: -------------------------------------------------------------------------------- 1 | gobasic is Copyrighted by Steve Kemp. 2 | Licensed under gpl-v2. Used by permission. 3 | You can find the original 'gobasic' at https://github.com/skx/gobasic 4 | -------------------------------------------------------------------------------- /eval/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /eval/for_loop.go: -------------------------------------------------------------------------------- 1 | // for_loop.go - Handles the state required for for-loops 2 | // 3 | // A for-loop looks like this: 4 | // 5 | // FOR i=START to END [STEP OFFSET] 6 | // .. 7 | // NEXT i 8 | // 9 | // The variable in the FOR-loop is unique. 10 | // 11 | //gobasic is Copyrighted by Steve Kemp. 12 | //Licensed under gpl-v2. Used by permission. 13 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 14 | 15 | package eval 16 | 17 | import "sync" 18 | 19 | // ForLoop is the structure used to record a for-loop 20 | type ForLoop struct { 21 | // variable the loop refers to 22 | id string 23 | 24 | // offset of the start of the loop-body 25 | offset int 26 | 27 | // start is the initial value of the variable at the start of the loop 28 | start float64 29 | 30 | // end is the terminating value of the variable 31 | end float64 32 | 33 | // increment is how much to step by 34 | step float64 35 | 36 | // is the loop finnished? 37 | finished bool 38 | } 39 | 40 | // Loops is the structure which holds ForLoop entries 41 | type Loops struct { 42 | // lock ensures we're thread-safe (ha!) 43 | lock sync.Mutex 44 | 45 | // data stores our data 46 | data map[string]ForLoop 47 | } 48 | 49 | // NewLoops creates a new for-loop holder 50 | func NewLoops() *Loops { 51 | return &Loops{lock: sync.Mutex{}, data: make(map[string]ForLoop)} 52 | } 53 | 54 | // Add stores a reference to a for-loop in our map 55 | func (l *Loops) Add(x ForLoop) { 56 | l.lock.Lock() 57 | defer l.lock.Unlock() 58 | 59 | l.data[x.id] = x 60 | } 61 | 62 | // Get returns a reference to a for-loop. 63 | func (l *Loops) Get(id string) ForLoop { 64 | l.lock.Lock() 65 | defer l.lock.Unlock() 66 | 67 | return (l.data[id]) 68 | } 69 | 70 | // Remove removes a reference to a for-loop. 71 | func (l *Loops) Remove(id string) { 72 | l.lock.Lock() 73 | defer l.lock.Unlock() 74 | 75 | delete(l.data, id) 76 | } 77 | 78 | // Empty returns true if we have no open FOR loop-references. 79 | func (l *Loops) Empty() bool { 80 | l.lock.Lock() 81 | defer l.lock.Unlock() 82 | 83 | return (len(l.data) == 0) 84 | } 85 | -------------------------------------------------------------------------------- /eval/stack.go: -------------------------------------------------------------------------------- 1 | // stack.go holds a simple stack which can hold ints. 2 | // 3 | // (This is used solely for the GOSUB/RETURN handler.) 4 | //gobasic is Copyrighted by Steve Kemp. 5 | //Licensed under gpl-v2. Used by permission. 6 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 7 | 8 | package eval 9 | 10 | import ( 11 | "errors" 12 | "sync" 13 | ) 14 | 15 | // Stack holds the stack-data, protected by a mutex 16 | type Stack struct { 17 | lock sync.Mutex 18 | s []int 19 | } 20 | 21 | // NewStack returns a new stack (for holding integers) 22 | func NewStack() *Stack { 23 | return &Stack{sync.Mutex{}, make([]int, 0)} 24 | } 25 | 26 | // Push adds a new item to our stack. 27 | func (s *Stack) Push(v int) { 28 | s.lock.Lock() 29 | defer s.lock.Unlock() 30 | 31 | s.s = append(s.s, v) 32 | } 33 | 34 | // Pop returns an item from our stack. 35 | func (s *Stack) Pop() (int, error) { 36 | s.lock.Lock() 37 | defer s.lock.Unlock() 38 | 39 | l := len(s.s) 40 | if l == 0 { 41 | return 0, errors.New("Empty Stack") 42 | } 43 | 44 | res := s.s[l-1] 45 | s.s = s.s[:l-1] 46 | return res, nil 47 | } 48 | 49 | // Empty returns `true` if our stack is empty. 50 | func (s *Stack) Empty() bool { 51 | 52 | s.lock.Lock() 53 | defer s.lock.Unlock() 54 | 55 | l := len(s.s) 56 | return (l == 0) 57 | } 58 | -------------------------------------------------------------------------------- /eval/vars.go: -------------------------------------------------------------------------------- 1 | // vars.go - Define an interface for getting/setting variables by name. 2 | // 3 | // NOTE: Names are assumed to be globally unique; there is no notion of scope. 4 | //gobasic is Copyrighted by Steve Kemp. 5 | //Licensed under gpl-v2. Used by permission. 6 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 7 | 8 | package eval 9 | 10 | import ( 11 | "sync" 12 | 13 | "basicbots/object" 14 | ) 15 | 16 | // Variables holds our state 17 | type Variables struct { 18 | // lock ensures we're thread-safe (ha!) 19 | lock sync.Mutex 20 | 21 | // data stores our data 22 | data map[string]object.Object 23 | } 24 | 25 | // NewVars handles a new variable-holder. 26 | func NewVars() *Variables { 27 | return &Variables{lock: sync.Mutex{}, data: make(map[string]object.Object)} 28 | } 29 | 30 | // Set stores the given value against the specified name. 31 | func (v *Variables) Set(name string, val object.Object) { 32 | v.lock.Lock() 33 | defer v.lock.Unlock() 34 | 35 | v.data[name] = val 36 | } 37 | 38 | // Get returns the value stored against the specified name. 39 | func (v *Variables) Get(name string) object.Object { 40 | v.lock.Lock() 41 | defer v.lock.Unlock() 42 | return (v.data[name]) 43 | } 44 | -------------------------------------------------------------------------------- /functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "basicbots/builtin" 5 | "basicbots/object" 6 | "fmt" 7 | "math" 8 | ) 9 | 10 | // Return the team number and set it is on a team. 11 | func FunctionTeam(env builtin.Environment, args []object.Object) object.Object { 12 | var t float64 13 | 14 | if !teams { 15 | return &object.NumberObject{Value: 0} 16 | } 17 | 18 | if current == 0 || current == 1 { 19 | t = 1 20 | } 21 | 22 | if current == 2 || current == 3 { 23 | t = 2 24 | } 25 | 26 | return &object.NumberObject{Value: t} 27 | } 28 | 29 | // Basic statement. LOCX returns the current Y location. 30 | func FunctionLocX(env builtin.Environment, args []object.Object) object.Object { 31 | X := Robots[current].X 32 | //if debug { 33 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionLocX() LOCX:%5.2f\n", current, Robots[current].X) 34 | //} 35 | return &object.NumberObject{Value: X} 36 | } 37 | 38 | // Basic statement. LOCY returns the current Y location 39 | func FunctionLocY(env builtin.Environment, args []object.Object) object.Object { 40 | y := Robots[current].Y 41 | //if debug { 42 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionLocY() LOCY:%5.2f\n", current, Robots[current].Y) 43 | //} 44 | return &object.NumberObject{Value: y} 45 | } 46 | 47 | // Basic statement. SPEED returns the current speed of the robot 48 | func FunctionSpeed(env builtin.Environment, args []object.Object) object.Object { 49 | speed := Robots[current].Speed 50 | //if debug { 51 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionSpeed() Speed:%5.2f\n", current, Robots[current].Speed) 52 | //} 53 | return &object.NumberObject{Value: speed} 54 | } 55 | 56 | // Basic statement. DAMAGE returns the current damage of the robot 57 | func FunctionDamage(env builtin.Environment, args []object.Object) object.Object { 58 | damage := float64(Robots[current].Damage) 59 | //if debug { 60 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionDamage() Damage:%d\n", current, Robots[current].Damage) 61 | //} 62 | return &object.NumberObject{Value: damage} 63 | } 64 | 65 | // Basic statement. DRIVE direction,speed sets speed and direction 66 | func FunctionDrive(env builtin.Environment, args []object.Object) object.Object { 67 | var s, d float64 68 | 69 | if args[0].Type() == object.NUMBER { 70 | d = args[0].(*object.NumberObject).Value 71 | } 72 | if args[1].Type() == object.NUMBER { 73 | s = args[1].(*object.NumberObject).Value 74 | } 75 | 76 | if s < 0.0 { 77 | s = 0.0 78 | } 79 | if s > 100.0 { 80 | s = 100.0 81 | } 82 | 83 | d = math.Mod(d, 360.0) 84 | if d < 0.0 { 85 | d += 360.0 86 | } 87 | if d > 360.0 { 88 | d -= 360.0 89 | } 90 | 91 | Robots[current].SpeedWanted = s 92 | Robots[current].HeadingWanted = d 93 | 94 | //if debug { 95 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionDrive heading:%5.2f speedwanted:%5.2f\n", current, Robots[current].HeadingWanted, Robots[current].SpeedWanted) 96 | //} 97 | 98 | return &object.NumberObject{Value: 0.0} 99 | } 100 | 101 | // Basic statement. SCAN direction,width. Scan the battlefield in direction with a width of +/- width 102 | func FunctionScan(env builtin.Environment, args []object.Object) object.Object { 103 | var angle, width float64 104 | 105 | if args[0].Type() == object.NUMBER { 106 | angle = args[0].(*object.NumberObject).Value 107 | } 108 | if args[1].Type() == object.NUMBER { 109 | width = args[1].(*object.NumberObject).Value 110 | } 111 | 112 | angle = math.Mod(angle, 360.0) 113 | if angle < 0 { 114 | angle += 360.0 115 | } 116 | if angle > 360.0 { 117 | angle -= 360 118 | 119 | } 120 | 121 | //if debug { 122 | // fmt.Fprintf(os.Stderr, "SCAN angle: %f\n", angle) 123 | //} 124 | 125 | if width > 10.0 { 126 | width = 10.0 127 | } 128 | if width < 2.0 { 129 | width = 2.0 130 | } 131 | Robots[current].Scan = angle 132 | Robots[current].Width = width 133 | 134 | checkAlive(current) 135 | if Robots[current].Status == DEAD { 136 | return &object.NumberObject{Value: 0.0} 137 | } 138 | x1 := Robots[current].X 139 | y1 := Robots[current].Y 140 | 141 | td := 1000.0 142 | for i := 0; i < numberOfRobots; i++ { 143 | // fmt.Println("i=", i, numberOfRobots) 144 | 145 | checkAlive(i) 146 | 147 | if Robots[i].Status == DEAD { 148 | continue 149 | } 150 | 151 | if i == current { 152 | continue 153 | } 154 | 155 | if teams { 156 | if current == 0 { 157 | if i == 1 { 158 | continue 159 | } 160 | } 161 | if current == 1 { 162 | if i == 0 { 163 | continue 164 | } 165 | } 166 | 167 | if current == 2 { 168 | if i == 3 { 169 | continue 170 | } 171 | } 172 | if current == 3 { 173 | if i == 2 { 174 | continue 175 | } 176 | } 177 | } 178 | 179 | x2 := Robots[i].X 180 | y2 := Robots[i].Y 181 | t := Scanner(angle, width, x1, y1, x2, y2) 182 | if t != 0.0 { 183 | if t < td { 184 | td = t 185 | } 186 | } 187 | } 188 | if td == 1000 { // would be fun if the limit of the scan was 100 units longer than missile range. maybe. 189 | td = 0 190 | } 191 | 192 | //if debug { 193 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionScan() angle:%5.2f width:%5.2f result:%5.2f\n", current, angle, width, td) 194 | //} 195 | 196 | return &object.NumberObject{Value: td} 197 | 198 | } 199 | 200 | // Basic statement. CANNON direction, range. Fire the cannon at angle and distance. Do nothing if no missiles available. 201 | func FunctionCannon(env builtin.Environment, args []object.Object) object.Object { 202 | 203 | var angle, rang float64 204 | 205 | if Robots[current].Reload != 0 { 206 | return &object.NumberObject{Value: 1.0} 207 | } 208 | 209 | if args[0].Type() == object.NUMBER { 210 | angle = args[0].(*object.NumberObject).Value 211 | } 212 | if args[1].Type() == object.NUMBER { 213 | rang = args[1].(*object.NumberObject).Value 214 | } 215 | if rang < 1 { 216 | // do nothing 217 | return &object.NumberObject{Value: 0.0} 218 | } 219 | if rang > MISSLERANGE { 220 | rang = MISSLERANGE 221 | } 222 | angle = math.Mod(angle, 360.0) 223 | if angle < 0 { 224 | angle += 360.0 225 | } 226 | if angle > 360.0 { 227 | angle -= 360.0 228 | } 229 | 230 | for m := 0; m < MAXMISSILES; m++ { 231 | if Missiles[current][m].Status == AVAILABLE && Missiles[current][m].Reload == 0 { 232 | Missiles[current][m].Status = FLYING 233 | Missiles[current][m].Distance = rang 234 | Missiles[current][m].Heading = angle 235 | Missiles[current][m].XOrigin = Robots[current].X 236 | Missiles[current][m].YOrigin = Robots[current].Y 237 | Missiles[current][m].XO = Robots[current].X 238 | Missiles[current][m].YO = Robots[current].Y 239 | Missiles[current][m].X = Robots[current].X 240 | Missiles[current][m].Y = Robots[current].Y 241 | //if debug { 242 | // fmt.Fprintf(os.Stderr, "Robot:%d FunctionCannon() missile:%d status:%d angle:%5.2f range:%5.2f OX:%5.2f OY:%5.2f\n", 243 | // current, m, Missiles[current][m].Status, angle, rang, Missiles[current][m].XOrigin, Missiles[current][m].YOrigin) 244 | //} 245 | Robots[current].Reload = ROBOTRELOAD 246 | break 247 | } 248 | } 249 | 250 | return &object.NumberObject{Value: 1.0} 251 | 252 | } 253 | 254 | func FunctionOut(env builtin.Environment, args []object.Object) object.Object { 255 | 256 | var outmessage string 257 | 258 | if args[0].Type() == object.STRING { 259 | outmessage = args[0].(*object.StringObject).Value 260 | } 261 | 262 | if current == 0 || current == 1 { 263 | select { 264 | case team1 <- outmessage: 265 | default: 266 | } 267 | } else { 268 | select { 269 | case team2 <- outmessage: 270 | default: 271 | } 272 | } 273 | return &object.NumberObject{Value: 0.0} 274 | } 275 | 276 | func FunctionIn(env builtin.Environment, args []object.Object) object.Object { 277 | var msg string 278 | 279 | if current == 0 || current == 1 { 280 | select { 281 | case msg = <-team1: 282 | // fmt.Println("received message", msg) 283 | default: 284 | } 285 | } else { 286 | select { 287 | case msg = <-team2: 288 | // fmt.Println("received message", msg) 289 | default: 290 | } 291 | } 292 | 293 | return &object.StringObject{Value: msg} 294 | 295 | } 296 | 297 | // STR converts a number to a string 298 | func FunctionSTRC(env builtin.Environment, args []object.Object) object.Object { 299 | 300 | var num, c int 301 | // Error? 302 | if args[0].Type() == object.ERROR { 303 | return args[0] 304 | } 305 | 306 | // Already a string? 307 | if args[0].Type() == object.STRING { 308 | return args[0] 309 | } 310 | 311 | if args[0].Type() == object.NUMBER { 312 | i := args[0].(*object.NumberObject).Value 313 | num = int(i) 314 | } 315 | 316 | if args[1].Type() == object.NUMBER { 317 | i := args[1].(*object.NumberObject).Value 318 | c = int(i) 319 | } 320 | 321 | // Get the value 322 | s := fmt.Sprintf("%%%d%d", c, num) 323 | 324 | return &object.StringObject{Value: s} 325 | } 326 | -------------------------------------------------------------------------------- /globals.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "basicbots/eval" 5 | "basicbots/tokenizer" 6 | 7 | "github.com/gdamore/tcell/v2" 8 | ) 9 | 10 | // Nasty global variables and constants go here 11 | 12 | // variables 13 | var debug bool // debug : Debug flag 14 | var trace bool // trace : Trace flag 15 | var battledisplay bool // battledisplay : true show graphics 16 | var bench bool // bench : do benchmarking 17 | 18 | var numberOfRobots int // numberOfRobots : The number of robots in this simulation 19 | var cycles int // cycles : The number of cpu cycles 20 | var Robots []Robot // Robots : Array of the robots 21 | var current int // current : The current active robot 22 | 23 | // basic interpreter 24 | var token []*tokenizer.Tokenizer // token : slice of tokienizers 25 | var evaluator []*eval.Interpreter // evaluator : Slice of Interpreters. 26 | 27 | var maxCycles int // maxCycles : Maximum numer of cycles per match. Can change with cli flag. 28 | 29 | var Missiles [MAXROBOTS][MAXMISSILES]Missile // Missile : Array of the missiles that can be used. 30 | 31 | var defStyle tcell.Style // defStyle : white on blank for text. Used in tcell. 32 | // var boxStyle tcell.Style // boxStyle : Leagacy purple 33 | var scr tcell.Screen // scr : tcell screen interface. Using global to keep from haveing go routines. 34 | 35 | var event = make(chan int) // event : Channel for getting out of tcell with escape key. 36 | var team1 = make(chan string) // team1 : Team 1 comm channel 37 | var team2 = make(chan string) // team2 : Team 2 comm channel 38 | 39 | //var team1message string 40 | //var team2message string 41 | 42 | var lox float64 // lox : scaling factor for battlefield to console area. 43 | var loy float64 // loy : scalling factor for battlefield to console area. 44 | var battleSizeX int // battleSizeX : Size of the battlescreen on the console in the X direction. 45 | var battleSizeY int // battleSizeY : Size of the battlescreen on the console in the Y direction. 46 | 47 | var cycledelay int64 // cycledelay : Delay in nanoseconds. Used in the battlescreen mode to slow down the play. 48 | 49 | var matchcount int // matchcount : Number of matches to play with current robots. Can not be used with 'battledisplay'. 50 | var tournamentMode bool // Chages the output to make parsing easier 51 | 52 | var etype int // etype : Holds the event type from tcell or function when needing to exit the program. 53 | var exiterror error // exiterror : Holds any error code that is causing the program to end. 54 | var teams bool // teams : true if the teams flag is set. 55 | 56 | //var timingTest bool // timingtest : True if tracking FPS 57 | 58 | //var startTime time.Time // startTime : For timing the average FPS 59 | //var timeBucket time.Duration // timeBucket : time for the number of robots on the playing field. 60 | /* 61 | CONSTANTS 62 | */ 63 | 64 | const VERSION = "v1.0.0" // VERSION : Version of the program. 65 | 66 | // Constants for cycles routines. Mainly movements. 67 | const ( 68 | MOVECLICKS = 50 // CLICKS : Number of cycles between moverobot and movemissile. 69 | //CLICK = 100 70 | ) 71 | 72 | // Constants for battlefield. 73 | const ( 74 | MAXX = 1000.0 // MAXX : Maximum size of the battlefield in the X direction. 75 | MAXY = 1000.0 // MAXY : Maximum size of the battlefield in the Y direction. 76 | ) 77 | 78 | // Constants for robot status. 79 | const ( 80 | MAXROBOTS = 4 // MAXROBOTS : The maximum number of robots allowed. 81 | ALIVE = 0 // ALIVE : Robot is functional. 82 | DEAD = 1 // DEAD : Robot is dead. 83 | ACCEL = 10.0 // ACCEL : Max Acceleration. 84 | ) 85 | 86 | // Constants for converting degrees to radians and back. 87 | const ( 88 | DEG2RAD = 0.0174532925 // DEG2RAD : multiply 0-360 degrees to get radians. 89 | RAD2DEG = 57.2957795130 // RAD2GEG : multiply radians to get degrees. 90 | ) 91 | 92 | // Constants for missile reload and movement. 93 | const ( 94 | MAXMISSILES = 2 // MAXMISSILES : Maximum number if missiles a robot can have on the battlefield at one time. 95 | MISSLERANGE = 700.0 // MISSLERANGE : Maxumum range of a missile. 96 | RELOAD = 15 // RELOAD : Number of of movrment cycles for a missile reload. 97 | MISSILESPEED = 500.0 // MISSILESPEED : Missiles move at full speed. No ramp up. 500 is 500% vs 100% for robots. 98 | ROBOTRELOAD = 5 // ROBOTRELOAD : Number of cycles for the robot to reload. Used to slow down the firing of the second missile. 99 | EXPLODECOUNT = 5 // EXPLODECOUNT : The number of movement cycles for the explosion to show in the battlescreen. 100 | ) 101 | 102 | // Constants for missile status 103 | const ( 104 | AVAILABLE = 1 // AVAILABLE : Missle is available for firing 105 | FLYING = 2 // FLYING : Missile is in flight. 106 | EXPLODE = 3 // EXPLODE : Missile is the process of exploding. 107 | EXPLODING = 4 // EXPLODING : Blowing up 108 | ) 109 | 110 | // Constants for robots running into things. 111 | const ( 112 | DAMAGEWALL = 2 // DAMAGEWALL : Amount of damage a robot will occure when it hits a wall. 113 | DAMAGECOL = 4 // DAMAGECOL : Amount of damage a robot will occure when it hits another robot. Both robots take damamge. 114 | ) 115 | 116 | // Constants for missle dmaage and blast radius. 117 | const ( 118 | MISFAR = 40 // MISFAR : Largest blast radius to cause damage. 119 | MISNEAR = 20 // MISNEAR : Medium blast radius to cause damage. 120 | MISCLOSE = 5 // MISCLOSE : Closest blast radius to cause damage. 121 | DAMFAR = 3 // DAMFAR : Damage to robot at between MISNEAR and MISFAR 122 | DAMNEAR = 5 // DAMNEAR : Damage to robot at between MISNEAR and MISCLOSE 123 | DAMCLOSE = 10 // DAMCLOSE : Damage to robot when at or below MISCLOSE 124 | ) 125 | 126 | // Constants for channel events 127 | const ( 128 | ESCKEY = 99 129 | BASICERROR = 98 130 | ) 131 | 132 | const benchbot = "10 DIM P(1000)\n100 GOSUB 15020\n110 GOTO 100\n9000 LET LLX = LOCX\n9005 LET LLY = LOCY\n9010 LET ANTT = ATN2 DY - LLY, DX - LLX\n9015 RETURN\n9050 LET LX = LOCX\n9055 LET LY = LOCY\n9060 LET TX = DX - LX\n9065 LET TY = DY - LY\n9070 LET DTT = SQR (TX*TX) + (TY*TY)\n9075 RETURN\n9100 LET DX = (RND 700) + 100\n9105 LET DY = (RND 700) + 100\n9110 RETURN\n15020 FOR I=2 TO 1000\n15030 LET P[I]=1\n15040 NEXT I\n15050 LET P[1]=0\n15060 FOR Q=2 TO 32\n15070 IF P[Q]=0 THEN GOTO 15110\n15075 LET QQ = Q * Q\n15080 FOR I = QQ TO 1000 STEP Q\n15090 LET P[I]=0\n15100 NEXT I\n15110 NEXT Q\n15120 REM\n15130 REM PAUSE 1000\n15140 LET C=0\n15150 FOR I=1 TO 1000\n15160 IF P[I]=0 THEN GOTO 15240\n15170 IF C<8 THEN GOTO 15200\n15180 REM \n15190 LET C=0\n15200 LET C=C+1\n15210 REM\n15220 REM\n15229 REM\n15230 CANNON I % 360, 700\n15232 LET SY = SCAN I % 360, 10\n15240 NEXT I\n15242 GOSUB 9100\n15245 GOSUB 9000\n15246 DRIVE ANTT, 100\n15247 GOSUB 9050\n15250 IF DTT < 50 THEN DRIVE ANTT, 0\n15250 RETURN\n" 133 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module basicbots 2 | 3 | go 1.17 4 | 5 | require github.com/gdamore/tcell/v2 v2.4.0 6 | 7 | require ( 8 | github.com/gdamore/encoding v1.0.0 // indirect 9 | github.com/lucasb-eyer/go-colorful v1.0.3 // indirect 10 | github.com/mattn/go-runewidth v0.0.10 // indirect 11 | github.com/rivo/uniseg v0.1.0 // indirect 12 | golang.org/x/sys v0.1.0 // indirect 13 | golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect 14 | golang.org/x/text v0.3.8 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= 2 | github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= 3 | github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM= 4 | github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= 5 | github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= 6 | github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 7 | github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= 8 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 9 | github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= 10 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 11 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 12 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 13 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 14 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 17 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 18 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 20 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 21 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 23 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 24 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 25 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 26 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 27 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 28 | golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= 29 | golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 30 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= 31 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 32 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 33 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 34 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 35 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= 36 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 37 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 38 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 39 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 40 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 41 | -------------------------------------------------------------------------------- /images/basicbots-logo-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/images/basicbots-logo-512x512.png -------------------------------------------------------------------------------- /images/example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/images/example1.gif -------------------------------------------------------------------------------- /images/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/images/example2.gif -------------------------------------------------------------------------------- /images/example4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/images/example4.gif -------------------------------------------------------------------------------- /images/example5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/images/example5.gif -------------------------------------------------------------------------------- /images/example6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/images/example6.gif -------------------------------------------------------------------------------- /latest/txtfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misterunix/basicbots/8f67f1303e3b940e0da0c5a5340b6409897eee66/latest/txtfile -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // Really, no need to document this. It is the main function. 13 | func main() { 14 | 15 | var err error 16 | 17 | var versionflag bool 18 | 19 | // Parse flags to expose the arguments 20 | flag.IntVar(&maxCycles, "c", 1500000, "Maximum number of cycles. Default is 1500000.") 21 | flag.BoolVar(&debug, "d", false, "Enable debug.") 22 | flag.BoolVar(&trace, "trace", false, "Enale program trace.") 23 | flag.BoolVar(&battledisplay, "b", false, "Enale battle display.") 24 | flag.IntVar(&matchcount, "m", 1, "Number of matches to simulate.") 25 | flag.BoolVar(&versionflag, "v", false, "Display version and credits.") 26 | flag.BoolVar(&teams, "t", false, "Enable teams.") 27 | flag.BoolVar(&bench, "bench", false, "Do benchmarking.") 28 | flag.BoolVar(&tournamentMode, "tt", false, "Output for tournament.") 29 | //flag.BoolVar(&timingTest, "time", false, "Turn on timing tests.") 30 | flag.Parse() 31 | 32 | if !bench { 33 | // if -v is set and there are no robots on the command line, call version() to output the version data. 34 | // I dont know why I am checking for robots. I really shouldnt. 35 | if versionflag || len(flag.Args()) == 0 { 36 | version() 37 | os.Exit(0) 38 | } 39 | } 40 | cycledelay = int64(time.Microsecond * 100) 41 | 42 | // Seed the random number generator. Doesn't need to be crypto strong. 43 | //rand.Seed(time.Now().UnixNano()) 44 | 45 | Robots = make([]Robot, MAXROBOTS) 46 | numberOfRobots = len(flag.Args()) 47 | if numberOfRobots > MAXROBOTS { // Check number of robots on the command line 48 | fmt.Fprintln(os.Stderr, "To many robots. Max:", MAXROBOTS) 49 | os.Exit(3) 50 | } 51 | 52 | /* 53 | if timingTest { 54 | battledisplay = true 55 | matchcount = 1 56 | } 57 | */ 58 | 59 | if bench { 60 | numberOfRobots = 4 61 | battledisplay = false 62 | matchcount = 500 63 | teams = false 64 | } 65 | 66 | // if teams is set, number of robots must be four 67 | if numberOfRobots != 4 && teams { 68 | fmt.Fprintf(os.Stderr, "Teams flag is set and the number of robots does not equal four.\n") 69 | os.Exit(3) 70 | } 71 | 72 | // If the battledisplay flag is set, make sure the matchcount = 1. Overriding -m 73 | if battledisplay { 74 | matchcount = 1 75 | initDisplay() // Create and Initialize the tcell module. 76 | } 77 | 78 | startTime := time.Now() // benching 79 | for match := 0; match < matchcount; match++ { 80 | err = InitRobots() 81 | if err != nil { 82 | fmt.Fprintln(os.Stderr, err) 83 | os.Exit(1) 84 | } 85 | 86 | exiterror = RunRobots() 87 | if etype != 0 { 88 | break 89 | } 90 | } 91 | 92 | // benching based on SGNIPS from https://crobots.deepthought.it/home.php?page=sgnips 93 | endDuration := time.Since(startTime).Seconds() 94 | if bench { 95 | ww := (1500000 * 500) / endDuration 96 | fmt.Printf("Bench: %d\n", int(ww)) 97 | os.Exit(0) 98 | } 99 | 100 | // if battledisplay flaf is set finialize tcell 101 | if battledisplay { 102 | // if the exit type is not a exit key then delay 10 seconds. Time to read the error or the score before restoring the screem 103 | if etype != ESCKEY { 104 | time.Sleep(10 * time.Second) 105 | } 106 | scr.Fini() 107 | } 108 | 109 | if exiterror != nil { 110 | fmt.Fprintf(os.Stderr, "%v\n", exiterror) // Some kind of error, most likely error in basic program 111 | os.Exit(1) // Exit without showing winner. 112 | } 113 | 114 | // Output the w,t,l,p for all robots 115 | for i := 0; i < numberOfRobots; i++ { 116 | space := strings.Repeat(" ", 20-len(Robots[i].Name)) 117 | //points := (Robots[i].Winner * 3) + Robots[i].Tie 118 | if tournamentMode { 119 | fmt.Printf("%s %d %d %d %f\n", Robots[i].Name, Robots[i].Winner, Robots[i].Tie, Robots[i].Lose, Robots[i].Points) 120 | } else { 121 | fmt.Printf("%s%s w:%05d t:%05d l:%05d p:%5.2f\n", Robots[i].Name, space, Robots[i].Winner, Robots[i].Tie, Robots[i].Lose, Robots[i].Points) 122 | } 123 | } 124 | if teams { 125 | t := "Team1" 126 | space := strings.Repeat(" ", 20-len(t)) 127 | fmt.Printf("%s%s w:%05d t:%05d l:%05d p:%5.2f\n", t, space, 128 | Robots[0].Winner, //|Robots[1].Winner, 129 | Robots[0].Tie, //|Robots[1].Tie, 130 | Robots[0].Lose, //|Robots[1].Lose, 131 | Robots[0].Points) //|Robots[1].Points) 132 | 133 | t = "Team2" 134 | space = strings.Repeat(" ", 20-len(t)) 135 | fmt.Printf("%s%s w:%05d t:%05d l:%05d p:%5.2f\n", t, space, 136 | Robots[2].Winner, //|Robots[3].Winner, 137 | Robots[2].Tie, //|Robots[3].Tie, 138 | Robots[2].Lose, //|Robots[3].Lose, 139 | Robots[2].Points) //|Robots[3].Points) 140 | } 141 | 142 | } 143 | 144 | // Print to stdout a version message along with copyright and other required messages. 145 | func version() { 146 | fmt.Printf("basicbots %s\n\n", VERSION) 147 | fmt.Printf("basicbots is created and copyrighted by William Jones\nand is licensed under GNU GPL v2\n\n") 148 | fmt.Printf("Modules used are:\n") 149 | fmt.Printf("gobasic by Steve Kemp and used by permission.\n") 150 | fmt.Printf("\tgobasic is licensed unther GPL v2 and can be \n\tfound at https://github.com/skx/gobasic\n\n") 151 | fmt.Printf("tcell by Garrett D'Amore and is licensend under Apache License 2.0\n\n") 152 | os.Exit(0) 153 | } 154 | -------------------------------------------------------------------------------- /makefile-nightly: -------------------------------------------------------------------------------- 1 | all : build 2 | 3 | docs: 4 | gomarkdoc -u > documentation/basicbots-dev.md 5 | 6 | build: 7 | mkdir basicbots 8 | mkdir basicbots/docs 9 | mkdir basicbots/robots 10 | mkdir basicbots/binaries 11 | cp documentation/BASIC.md basicbots/docs/ 12 | cp documentation/BASICBOTS.md basicbots/docs/ 13 | cp robots/* basicbots/robots 14 | env GOOS=linux GOARCH=amd64 go build -o basicbots/binaries/basicbots64-linux 15 | env CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o basicbots/binaries/basicbots64-windows.exe 16 | tar cvfz latest/linux64-nightly.tgz basicbots/docs/ basicbots/robots/ basicbots/binaries/basicbots64-linux 17 | zip latest/windows64-nightly.zip basicbots/robots/* basicbots/docs/* basicbots/binaries/basicbots64-windows.exe 18 | rm -rf basicbots -------------------------------------------------------------------------------- /makefile-no: -------------------------------------------------------------------------------- 1 | all : build 2 | 3 | docs: 4 | gomarkdoc -u > documentation/basicbots-dev.md 5 | 6 | build: 7 | # mkdir -p basicbots 8 | mkdir -p basicbots/docs 9 | mkdir -p basicbots/robots 10 | mkdir -p basicbots/binaries 11 | cp documentation/BASIC.md basicbots/docs/ 12 | cp documentation/BASICBOTS.md basicbots/docs/ 13 | cp robots/* basicbots/robots 14 | env GOOS=linux GOARCH=amd64 go build -o basicbots/binaries/basicbots64-linux 15 | env CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o basicbots/binaries/basicbots64-windows.exe 16 | env CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -o basicbots/binaries/basicbots64-darwin 17 | sha256sum basicbots/binaries/basicbots64-linux 18 | sha256sum basicbots/binaries/basicbots64-windows.exe 19 | sha256sum basicbots/binaries/basicbots64-darwin 20 | tar cvz latest/linux64-0.0.2c.tgz basicbots/docs/ basicbots/robots/ basicbots/binaries/basicbots64-linux 21 | tar cvz latest/darwin64-0.0.2c.tgz basicbots/docs/ basicbots/robots/ basicbots/binaries/basicbots64-darwin 22 | zip -q latest/windows64-0.0.2c.zip basicbots/robots/* basicbots/docs/* basicbots/binaries/basicbots64-windows.exe 23 | sha256sum latest/linux64-0.0.2c.tgz 24 | sha256sum latest/darwin64-0.0.2c.tgz 25 | sha256sum latest/windows64-0.0.2c.zip 26 | rm -rf basicbots 27 | -------------------------------------------------------------------------------- /makewindows.bat-no: -------------------------------------------------------------------------------- 1 | mkdir basicbots 2 | mkdir basicbots\robots 3 | mkdir basicbots\docs 4 | copy robots\* basicbots\robots 5 | env CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o basicbots/basicbots64.exe 6 | zip windows64.zip basicbots\* basicbots\robots\* basicbots\docs\* 7 | rmdir /Q /S basicbots 8 | copy windows64.zip latest\ 9 | -------------------------------------------------------------------------------- /missile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Missle : type struct for holding missle variables. 4 | type Missile struct { 5 | X float64 // X : Current X location of the missile. 6 | Y float64 // Y : Current Y location of the missile. 7 | XOrigin float64 // XOrigin : Origin point from the last X location. Used in movement for directions. 8 | YOrigin float64 // YOrigin : Origin point from the lasy Y location. Used in movement for ditections. 9 | XO float64 // XO : Origin point from the last X location. Used in movement for directions. 10 | YO float64 // YO : Origin point from the lasy Y location. Used in movement for ditections. 11 | XPlotOld int // XPlotOld : Last battlefield X position. Used for erasing the old marker. 12 | YPlotOld int // YPlotOld : Last battlefield Y position. Used for erasing the old marker. 13 | Heading float64 // Heading : Heading of the missile. 14 | Distance float64 // Distance : The set distance at which the missile explodes. 15 | Status int // Status : The status of the missile. Available,flying,exploding. 16 | Reload int // Reload : Movement clicks until reloaded. 17 | ExplodeCount int // ExplodeCount : Time in motion cycles to have explosion showing. 18 | } 19 | -------------------------------------------------------------------------------- /movement.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | // moverobot : Move robots every motion cycle. 8 | // Take damage, detect dead robots, detect for collision. 9 | func moverobot() { 10 | 11 | for i := 0; i < numberOfRobots; i++ { 12 | checkAlive(i) 13 | if Robots[i].Status == DEAD { 14 | continue 15 | } 16 | 17 | // seems I need to check for walls before doing anything 18 | if Robots[i].X < 0.0 { 19 | Robots[i].X = 0.0 20 | Robots[i].XOrigin = Robots[i].X 21 | Robots[i].Damage += DAMAGEWALL 22 | Robots[i].Speed = 0.0 23 | Robots[i].SpeedWanted = 0.0 24 | Robots[i].SpeedHold = 0.0 25 | Robots[i].HeadingWanted = Robots[i].Heading 26 | } 27 | if Robots[i].Y < 0.0 { 28 | Robots[i].Y = 0.0 29 | Robots[i].YOrigin = Robots[i].Y 30 | Robots[i].Damage += DAMAGEWALL 31 | Robots[i].Speed = 0.0 32 | Robots[i].SpeedWanted = 0.0 33 | Robots[i].SpeedHold = 0.0 34 | Robots[i].HeadingWanted = Robots[i].Heading 35 | 36 | } 37 | if Robots[i].X > MAXX { 38 | Robots[i].X = MAXX 39 | Robots[i].XOrigin = Robots[i].X 40 | Robots[i].Damage += DAMAGEWALL 41 | Robots[i].Speed = 0.0 42 | Robots[i].SpeedWanted = 0.0 43 | Robots[i].SpeedHold = 0.0 44 | Robots[i].HeadingWanted = Robots[i].Heading 45 | } 46 | if Robots[i].Y > MAXY { 47 | Robots[i].Y = MAXY 48 | Robots[i].YOrigin = Robots[i].Y 49 | Robots[i].Damage += DAMAGEWALL 50 | Robots[i].Speed = 0.0 51 | Robots[i].SpeedWanted = 0.0 52 | Robots[i].SpeedHold = 0.0 53 | Robots[i].HeadingWanted = Robots[i].Heading 54 | } 55 | 56 | if Robots[i].Heading != Robots[i].HeadingWanted { 57 | if Robots[i].Speed > 50.0 { 58 | if Robots[i].SpeedHold == 0.0 { 59 | Robots[i].SpeedHold = Robots[i].SpeedWanted 60 | } 61 | Robots[i].SpeedWanted = 50.0 62 | } else { 63 | Robots[i].Heading = Robots[i].HeadingWanted 64 | if Robots[i].SpeedHold != 0.0 { 65 | Robots[i].SpeedWanted = Robots[i].SpeedHold 66 | Robots[i].SpeedHold = 0.0 67 | } 68 | } 69 | } 70 | 71 | if Robots[i].SpeedWanted > Robots[i].Speed { 72 | if Robots[i].Speed < 1.0 { 73 | Robots[i].Speed = 1.1 74 | } 75 | 76 | t := Robots[i].Speed 77 | velocity := math.Log10(t) / 1.5 78 | Robots[i].Speed += velocity 79 | if Robots[i].Speed > Robots[i].SpeedWanted { 80 | Robots[i].Speed = Robots[i].SpeedWanted 81 | } 82 | } 83 | 84 | if Robots[i].SpeedWanted < Robots[i].Speed { 85 | t := Robots[i].Speed 86 | if t > 1 && Robots[i].SpeedWanted != 0 { 87 | velocity := math.Log10(t) / 1.2 88 | Robots[i].Speed -= velocity 89 | if Robots[i].Speed < Robots[i].SpeedWanted { 90 | Robots[i].Speed = Robots[i].SpeedWanted 91 | } 92 | } else { 93 | Robots[i].Speed = 0.0 94 | } 95 | } 96 | 97 | if Robots[i].Speed < 0.0 { 98 | Robots[i].Speed = 0.0 99 | } 100 | if Robots[i].Speed > 100.0 { 101 | Robots[i].Speed = 100.0 102 | } 103 | 104 | Robots[i].X = Robots[i].XOrigin + math.Cos(Robots[i].Heading*DEG2RAD)*(Robots[i].Speed/100.0) 105 | Robots[i].Y = Robots[i].YOrigin + math.Sin(Robots[i].Heading*DEG2RAD)*(Robots[i].Speed/100.0) 106 | 107 | Robots[i].XOrigin = Robots[i].X 108 | Robots[i].YOrigin = Robots[i].Y 109 | 110 | //if debug { 111 | // fmt.Fprintf(os.Stderr, "Robot:%d cycle:%d Hd:%5.2f HdW:%5.2f x:%5.2f y:%5.2f \n", 112 | //i, cycles, Robots[i].Heading, Robots[i].HeadingWanted, Robots[i].X, Robots[i].Y) 113 | //} 114 | 115 | for rn := 0; rn < numberOfRobots; rn++ { 116 | if rn == i { 117 | continue 118 | } 119 | checkAlive(rn) 120 | if Robots[rn].Status == DEAD { 121 | continue 122 | } 123 | 124 | if int(Robots[i].X) == int(Robots[rn].X) && int(Robots[i].Y) == int(Robots[rn].Y) { 125 | a1 := Robots[i].Heading 126 | a2 := a1 - 180 127 | if a2 < 0 { 128 | a2 += 360.0 129 | } 130 | if a2 > 360 { 131 | a2 -= 360.0 132 | } 133 | 134 | // get the offset of the current robot and add the inverse to the one hit 135 | tx := math.Cos(Robots[i].Heading*DEG2RAD) * 2.0 136 | ty := math.Sin(Robots[i].Heading*DEG2RAD) * 2.0 137 | Robots[rn].X += tx 138 | Robots[rn].Y += ty 139 | Robots[rn].XOrigin = Robots[rn].X 140 | Robots[rn].YOrigin = Robots[rn].Y 141 | Robots[rn].Speed = 0.0 142 | Robots[rn].SpeedWanted = 0.0 143 | Robots[i].Speed = 0.0 144 | Robots[i].SpeedWanted = 0.0 145 | Robots[i].Damage += DAMAGECOL 146 | Robots[rn].Damage += DAMAGECOL 147 | // move unit back one from the direction they came and set speed to 0 148 | } 149 | 150 | // Check current robot damage and break out of current collision loop 151 | checkAlive(i) 152 | if Robots[i].Status == DEAD { 153 | break 154 | } 155 | } 156 | } 157 | 158 | } 159 | 160 | // movemissile : move all flying missiles per motion click. Check for blast damage, htting wall, etc. 161 | func movemissile() { 162 | //if debug { 163 | // fmt.Fprintf(os.Stderr, "Cycles %d movemissile()\n", cycles) 164 | //} 165 | // missiles fly even if the robot that shot it is dead. 166 | for r := 0; r < numberOfRobots; r++ { 167 | 168 | for m := 0; m < MAXMISSILES; m++ { 169 | 170 | if Missiles[r][m].Status == AVAILABLE { 171 | if Missiles[r][m].Reload > 0 { 172 | Missiles[r][m].Reload-- 173 | continue 174 | } else { 175 | Missiles[r][m].Reload = RELOAD 176 | } 177 | } 178 | 179 | if Missiles[r][m].Status == FLYING { 180 | // v := math.Log10(MISSILESPEED) 181 | Missiles[r][m].X = Missiles[r][m].XO + math.Cos(Missiles[r][m].Heading*DEG2RAD)*(MISSILESPEED/100.0) 182 | Missiles[r][m].Y = Missiles[r][m].YO + math.Sin(Missiles[r][m].Heading*DEG2RAD)*(MISSILESPEED/100.0) 183 | Missiles[r][m].XO = Missiles[r][m].X 184 | Missiles[r][m].YO = Missiles[r][m].Y 185 | //if debug { 186 | // fmt.Fprintf(os.Stderr, "Robot:%d movemissile() move Missile:%d X:%5.2f Y:%5.2f xo:%5.2f yo:%5.2f range:%5.2f\n", 187 | // r, m, Missiles[r][m].X, Missiles[r][m].Y, Missiles[r][m].XOrigin, Missiles[r][m].YOrigin, Missiles[r][m].Distance) 188 | // } 189 | if Missiles[r][m].X <= 0.0 { 190 | Missiles[r][m].X = 0.0 191 | Missiles[r][m].Status = EXPLODE 192 | } 193 | if Missiles[r][m].Y <= 0.0 { 194 | Missiles[r][m].Y = 0.0 195 | Missiles[r][m].Status = EXPLODE 196 | } 197 | if Missiles[r][m].X >= MAXX { 198 | Missiles[r][m].X = MAXX - 1 199 | Missiles[r][m].Status = EXPLODE 200 | } 201 | if Missiles[r][m].Y >= MAXY { 202 | Missiles[r][m].Y = MAXY - 1 203 | Missiles[r][m].Status = EXPLODE 204 | } 205 | 206 | tx := Missiles[r][m].X - Missiles[r][m].XOrigin 207 | ty := Missiles[r][m].Y - Missiles[r][m].YOrigin 208 | d := math.Sqrt((tx * tx) + (ty * ty)) 209 | if d >= Missiles[r][m].Distance || d >= MISSLERANGE { 210 | Missiles[r][m].Status = EXPLODE 211 | //if debug { 212 | // fmt.Fprintf(os.Stderr, "Robot:%d movemissile() Distance test Missile:%d distance %5.2f range:%5.2f x:%5.2f xo:%5.2f y:%5.2f yo:%5.2f tx:%5.2f ty:%5.2f\n", 213 | // r, m, d, Missiles[r][m].Distance, Missiles[r][m].X, Missiles[r][m].XOrigin, Missiles[r][m].Y, Missiles[r][m].YOrigin, tx, ty) 214 | //} 215 | } 216 | 217 | } 218 | 219 | if Missiles[r][m].Status == EXPLODING { 220 | //if debug { 221 | // fmt.Fprintf(os.Stderr, "Robot:%d movemissile() Missile:%d Exploding1 X:%5.2f Y:%5.2f\n", r, m, Missiles[r][m].X, Missiles[r][m].Y) 222 | //} 223 | if Missiles[r][m].ExplodeCount == EXPLODECOUNT { 224 | Missiles[r][m].Reload = RELOAD 225 | Missiles[r][m].Status = AVAILABLE 226 | Missiles[r][m].ExplodeCount = 0 227 | } else { 228 | Missiles[r][m].ExplodeCount++ 229 | } 230 | } 231 | 232 | if Missiles[r][m].Status == EXPLODE { 233 | Missiles[r][m].Status = EXPLODING 234 | //if debug { 235 | // fmt.Fprintf(os.Stderr, "Robot:%d movemissile() Missile:%d Exploding X:%5.2f Y:%5.2f\n", r, m, Missiles[r][m].X, Missiles[r][m].Y) 236 | // } 237 | 238 | for i := 0; i < numberOfRobots; i++ { 239 | checkAlive(i) 240 | if Robots[i].Status == DEAD { 241 | continue 242 | } 243 | if i == r { 244 | continue 245 | } 246 | tx := Missiles[r][m].X - Robots[i].X 247 | ty := Missiles[r][m].Y - Robots[i].Y 248 | d := math.Sqrt((tx * tx) + (ty * ty)) 249 | switch { 250 | case d <= MISCLOSE: 251 | Robots[i].Damage += DAMCLOSE 252 | case d <= MISNEAR: 253 | Robots[i].Damage += DAMNEAR 254 | case d <= MISFAR: 255 | Robots[i].Damage += DAMFAR 256 | } 257 | 258 | checkAlive(i) 259 | 260 | } 261 | 262 | } 263 | 264 | } 265 | 266 | } 267 | 268 | } 269 | -------------------------------------------------------------------------------- /object/COPYRIGHT: -------------------------------------------------------------------------------- 1 | gobasic is Copyrighted by Steve Kemp. 2 | Licensed under gpl-v2. Used by permission. 3 | You can find the original 'gobasic' at https://github.com/skx/gobasic 4 | -------------------------------------------------------------------------------- /object/object.go: -------------------------------------------------------------------------------- 1 | // Package object contains code to store values passed to/from BASIC. 2 | // 3 | // Go allows a rich number of types, but when interpreting BASIC programs 4 | // we only support numbers & strings, as well as two-dimensional arrays 5 | // containing those values. 6 | // 7 | // Note that numbers are stored as `float64`, to allow holding both 8 | // integers and floating-point numbers. 9 | // 10 | //gobasic is Copyrighted by Steve Kemp. 11 | //Licensed under gpl-v2. Used by permission. 12 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 13 | 14 | package object 15 | 16 | import ( 17 | "fmt" 18 | ) 19 | 20 | // Type describes the type of an object. 21 | type Type string 22 | 23 | // These are our object-types. 24 | const ( 25 | ERROR = "ERROR" 26 | NUMBER = "NUMBER" 27 | STRING = "STRING" 28 | ARRAY = "ARRAY" 29 | ) 30 | 31 | // Object is the interface that our types must implement. 32 | type Object interface { 33 | 34 | // Type returns the type of the object. 35 | Type() Type 36 | 37 | // String converts the object to a printable version for debugging. 38 | String() string 39 | } 40 | 41 | // ArrayObject holds an array. 42 | // 43 | // We allow only two-dimensional arrays, and the size is set at the time 44 | // the array is constructed. 45 | type ArrayObject struct { 46 | 47 | // We store objects in our array 48 | Contents []Object 49 | 50 | // X is the X-size of the array, fixed at creation-time 51 | X int 52 | 53 | // Y is the Y-size of the array, fixed at creation-time. 54 | Y int 55 | } 56 | 57 | // Type returns the type of this object. 58 | func (a *ArrayObject) Type() Type { 59 | return ARRAY 60 | } 61 | 62 | // Array creates a new array of the given dimensions 63 | func Array(x int, y int) *ArrayObject { 64 | 65 | // Our semantics ensure that we allow "0-N". 66 | if x != 0 { 67 | x++ 68 | } 69 | if y != 0 { 70 | y++ 71 | } 72 | 73 | // setup the sizes 74 | a := &ArrayObject{X: x, Y: y} 75 | 76 | // for each entry ensure we store a value. 77 | var c int 78 | if x == 0 { 79 | c = y 80 | } else { 81 | c = x * y 82 | } 83 | 84 | // we default to "0" 85 | for c >= 0 { 86 | a.Contents = append(a.Contents, Number(0)) 87 | c-- 88 | } 89 | 90 | return a 91 | } 92 | 93 | // Get the value at the given X,Y coordinate 94 | func (a *ArrayObject) Get(x int, y int) Object { 95 | offset := int(x*a.X + y) 96 | 97 | if a.X == 0 && offset >= a.Y { 98 | return &ErrorObject{Value: "Get-Array access out of bounds (Y)"} 99 | } 100 | if (a.X != 0) && (offset > a.X*a.Y) { 101 | return &ErrorObject{Value: "Get-Array access out of bounds (X,Y)"} 102 | } 103 | if offset < 0 { 104 | return &ErrorObject{Value: "Get-Array access out of bounds (negative index)"} 105 | } 106 | if offset > len(a.Contents) { 107 | return &ErrorObject{Value: "Get-Array access out of bounds (LEN)"} 108 | } 109 | return (a.Contents[offset]) 110 | } 111 | 112 | // Set the value at the given X,Y coordinate 113 | func (a *ArrayObject) Set(x int, y int, obj Object) Object { 114 | offset := int(x*a.X + y) 115 | 116 | if a.X == 0 && offset >= a.Y { 117 | return &ErrorObject{Value: "Set-Array access out of bounds (Y)"} 118 | } 119 | if (a.X != 0) && (offset > a.X*a.Y) { 120 | return &ErrorObject{Value: "Set-Array access out of bounds (X,Y)"} 121 | } 122 | if offset < 0 { 123 | return &ErrorObject{Value: "Set-Array access out of bounds (negative index)"} 124 | } 125 | if offset > len(a.Contents) { 126 | return &ErrorObject{Value: "Set-Array access out of bounds (LEN)"} 127 | } 128 | 129 | a.Contents[offset] = obj 130 | return obj 131 | } 132 | 133 | // String returns the string-contents of the string 134 | func (a *ArrayObject) String() string { 135 | 136 | out := fmt.Sprintf("Array{X:%d, Y:%d, <%v>}", 137 | a.X, a.Y, a.Contents) 138 | return (out) 139 | } 140 | 141 | // StringObject holds a string. 142 | type StringObject struct { 143 | 144 | // Value is the value our object wraps. 145 | Value string 146 | } 147 | 148 | // Type returns the type of this object. 149 | func (s *StringObject) Type() Type { 150 | return STRING 151 | } 152 | 153 | // String returns a string representation of this object. 154 | func (s *StringObject) String() string { 155 | return (fmt.Sprintf("Object{Type:string, Value:%s}", s.Value)) 156 | } 157 | 158 | // String is a helper for creating a new string-object with the given value. 159 | func String(val string) *StringObject { 160 | return &StringObject{Value: val} 161 | } 162 | 163 | // NumberObject holds a number. 164 | type NumberObject struct { 165 | 166 | // Value is the value our object wraps. 167 | Value float64 168 | } 169 | 170 | // Type returns the type of this object. 171 | func (s *NumberObject) Type() Type { 172 | return NUMBER 173 | } 174 | 175 | // String returns a string representation of this object. 176 | func (s *NumberObject) String() string { 177 | return (fmt.Sprintf("Object{Type:number, Value:%f}", s.Value)) 178 | } 179 | 180 | // Number is a helper for creating a new number-object with the given value. 181 | func Number(val float64) *NumberObject { 182 | return &NumberObject{Value: val} 183 | } 184 | 185 | // ErrorObject holds a string, which describes an error 186 | type ErrorObject struct { 187 | 188 | // Value is the message our object wraps. 189 | Value string 190 | } 191 | 192 | // Error is a helper for creating a new error-object with the given message. 193 | func Error(format string, args ...interface{}) *ErrorObject { 194 | msg := fmt.Sprintf(format, args...) 195 | return &ErrorObject{Value: msg} 196 | } 197 | 198 | // Type returns the type of this object. 199 | func (s *ErrorObject) Type() Type { 200 | return ERROR 201 | } 202 | 203 | // String returns a string representation of this object. 204 | func (s *ErrorObject) String() string { 205 | return (fmt.Sprintf("Object{Type:error, Value:%s}", s.Value)) 206 | } 207 | -------------------------------------------------------------------------------- /robots.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Robot : type struct for holding robot variables 4 | type Robot struct { 5 | Name string // Name : The name of the robot 6 | 7 | X float64 // X : Current X of the robot. 8 | Y float64 // Y : Current Y of the robot. 9 | XOrigin float64 // XOrigin : orgin x location. Used in movement. 10 | YOrigin float64 // YOrigin : orgin y location. Used in movement. 11 | XPlotOld int // XPlotOld : Last X plot on the battlefield. Used to remove the past marker. 12 | YPlotOld int // YPlotOld : Last Y plot on the battlefield. Used to remove the past marker. 13 | 14 | Damage int // Damage : Current state of the damage of the robot 15 | 16 | Speed float64 // Speed : Current speed of the robot. 17 | SpeedWanted float64 // SpeedWanted : The desired speed of the robot. 18 | SpeedHold float64 // SpeedHold : Holds the previous speed while in a turn. 19 | 20 | Heading float64 // Heading : Current heading of the robot 21 | HeadingWanted float64 // HeadingWanted : The disired heading of the robot. 22 | Distance float64 // Distance : The Distance the robot has traveled from the origin point. 23 | 24 | Scan float64 // Scan : Scan heading. 0-360 25 | Width float64 // Width : Scan width 2-10 26 | 27 | Cannon float64 // Cannon : Vannon heading 28 | Reload int // Reload : Countdown until cannon reload is complete 29 | 30 | Status int // Status : Status of the robot. Dead or Alive. 31 | 32 | Winner int // Winner : Number of wins 33 | Lose int // Lose : Number off times lost 34 | Tie int // Tie : Number of times tied 35 | Points float64 // Points : Number of points this run. 36 | 37 | Program []byte // Program : Byte slice holding the program's text file. 38 | } 39 | -------------------------------------------------------------------------------- /robots/blaster.bas: -------------------------------------------------------------------------------- 1 | 1 REM pick a spot, run to it, shoot while moving, lead shots 2 | 2 LET DX = 0 3 | 3 LET DY = 0 4 | 4 LET DMG = 0 5 | 5 LET ODMG = 0 6 | 6 LET LX = 0 7 | 7 LET LY = 0 8 | 8 LET DTT = 0 9 | 9 LET ATT = 0 10 | 10 LET RSA = 0 11 | 11 let RSW = 5 12 | 13 | 14 | 100 REM main loop 15 | 110 DMG = DAMAGE 16 | 120 GOSUB 2000 : REM pick spot 17 | 130 GOSUB 2600 : REM get angle 18 | 140 DRIVE ATT, 100 19 | 150 GOSUB 2500 : REM get distance 20 | 160 IF DTT < 50 THEN GOTO 200 21 | 160 IF DMG <> ODMG THEN GOTO 250 22 | 170 GOSUB 2700 23 | 180 GOTO 150 24 | 25 | 200 DRIVE ATT,0 26 | 210 GOTO 120 27 | 28 | 249 REM damage! 29 | 250 LET ODMG = DMG 30 | 260 GOSUB 2000 : REM pick spot 31 | 270 GOSUB 2600 : REM get angle 32 | 275 DRIVE ATT, 100 33 | 280 GOSUB 2500 : REM get distance 34 | 290 IF DTT < 100 THEN G0T0 350 35 | 300 GOSUB 2700 36 | 310 RETURN 37 | 350 DRIVE ATT,0 38 | 360 RETURN 39 | 40 | 2000 REM pick random DX,DY 41 | 2010 LET DX = (RND 700) + 100 42 | 2020 LET DY = (RND 700) + 100 43 | 2030 RETURN 44 | 45 | 2500 REM get distance to target 46 | 2510 LET LX = LOCX 47 | 2515 LET LY = LOCY 48 | 2520 LET TX = DX - LOCX 49 | 2521 LET TY = DY - LY 50 | 2530 LET DTT = SQR (TX*TX) + (TY*TY) 51 | 2540 RETURN 52 | 53 | 2600 REM angle to target 54 | 2610 LET LX = LOCX 55 | 2620 LET LY = LOCY 56 | 2630 LET ATT = ATN2 DY-LY, DX - LX 57 | 2640 RETURN 58 | 59 | 2700 REM scan and shoot 60 | 2710 LET SDTT = SCAN RSA,RSW 61 | 2720 IF SDTT > 0 AND SDTT < 700 THEN GOTO 2800 62 | 2730 LET RSA = RSA + RSW 63 | 2740 RETURN 64 | 65 | 2800 LET DTTN = SCAN RSA,RSW 66 | 2810 LET DIF = DTTN + (DTTN - SDTT) 67 | 2820 CANNON RSA, DIF 68 | 2830 RETURN -------------------------------------------------------------------------------- /robots/corner-runner.bas: -------------------------------------------------------------------------------- 1 | 1 REM corner-runner.bas 2 | 2 REM move to a corner and scan and shoot 3 | 4 | 10 LET SPEEDHIGH = 100 5 | 11 LET SPEEDMID = 50 6 | 12 LET SPEEDLOW = 10 7 | 13 LET SPEEDSTOP = 0 8 | 14 LET DAM = 0 9 | 10 | 11 | 100 LET Q = RND 4 12 | 105 IF Q = 0 THEN GOTO 200 13 | 110 IF Q = 1 THEN GOTO 220 14 | 115 IF Q = 2 THEN GOTO 240 15 | 120 IF Q = 3 THEN GOTO 260 16 | 130 GOTO 240 17 | 18 | 200 LET DX = 100 19 | 205 LET DY = 100 20 | 206 LET TS = 0 21 | 207 LET BS = 90 22 | 210 GOTO 400 23 | 24 | 220 LET DX = 900 25 | 225 LET DY = 100 26 | 226 LET TS = 90 27 | 227 LET BS = 180 28 | 230 GOTO 400 29 | 30 | 240 LET DX = 900 31 | 245 LET DY = 900 32 | 246 LET TS = 180 33 | 247 LET BS = 270 34 | 250 GOTO 400 35 | 36 | 260 LET DX = 100 37 | 265 LET DY = 900 38 | 266 LET TS = 270 39 | 267 LET BS = 360 40 | 270 GOTO 400 41 | 42 | 43 | 400 GOSUB 2000 44 | 420 DRIVE A, SPEEDSTOP 45 | 430 LET DAM = DAMAGE 46 | 440 GOSUB 3000 47 | 450 GOTO 100 48 | 49 | 50 | 51 | 52 | 2000 REM drive to around DX,DY 53 | 2003 GOSUB 2110 : REM get angle to target 54 | 2004 LET AA = A 55 | 2005 DRIVE AA,SPEEDHIGH 56 | 2006 GOSUB 5000 : REM get distance to target 57 | 2010 IF D < 50 THEN GOTO 2030 58 | 2015 IF D < 100 THEN GOTO 2040 59 | 2020 IF D >= 100 THEN GOTO 2005 60 | 2030 DRIVE AA,SPEEDSTOP 61 | 2035 RETURN 62 | 2040 DRIVE AA, SPEEDMID 63 | 2045 GOTO 2006 64 | 65 | 66 | 2100 REM get angle to target 67 | 2110 LET LX = LOCX 68 | 2115 LET LY = LOCY 69 | 2120 LET A = ATN2 DY-LY, DX-LX 70 | 2125 RETURN 71 | 72 | 3000 REM scan and shoot 73 | 3001 REM from TS to BS 74 | 3010 LET SCANANGLE = TS 75 | 3015 IF DAM <> DAMAGE THEN RETURN 76 | 3020 LET RANGE = SCAN SCANANGLE, 5 77 | 3025 IF RANGE < 700 AND RANGE > 40 THEN GOTO 3050 78 | 3030 LET SCANANGLE = SCANANGLE + 5 79 | 3035 IF SCANANGLE >= BS THEN RETURN 80 | 3040 GOTO 3015 81 | 3050 CANNON SCANANGLE, RANGE 82 | 3055 GOTO 3015 83 | 84 | 5000 REM calculate distance 85 | 5001 REM DX,DY is the destination 86 | 5002 REM D is the distance 87 | 5005 LET LX = LOCX 88 | 5010 LET LY = LOCY 89 | 5015 LET TX = DX-LX 90 | 5020 LET TY = DY-LY 91 | 5025 LET D = SQR (TX*TX)+(TY*TY) 92 | 5030 RETURN -------------------------------------------------------------------------------- /robots/corner-v2.bas: -------------------------------------------------------------------------------- 1 | 1 REM corner-runner.bas 2 | 2 REM move to a corner and scan and shoot 3 | 4 | 10 LET SPEEDHIGH = 100 5 | 11 LET SPEEDMID = 50 6 | 12 LET SPEEDLOW = 10 7 | 13 LET SPEEDSTOP = 0 8 | 14 LET DAM = 0 9 | 15 LET ROAMSCAN = 0 10 | 16 LET ROAMDIST = 0 11 | 17 LET ROAMWIDTH = 3 12 | 13 | 100 LET Q = RND 4 14 | 105 IF Q = 0 THEN GOTO 200 15 | 110 IF Q = 1 THEN GOTO 220 16 | 115 IF Q = 2 THEN GOTO 240 17 | 120 IF Q = 3 THEN GOTO 260 18 | 130 GOTO 240 19 | 20 | 200 LET DX = 100 21 | 205 LET DY = 100 22 | 206 LET TS = 0 23 | 207 LET BS = 90 24 | 210 GOTO 400 25 | 26 | 220 LET DX = 900 27 | 225 LET DY = 100 28 | 226 LET TS = 90 29 | 227 LET BS = 180 30 | 230 GOTO 400 31 | 32 | 240 LET DX = 900 33 | 245 LET DY = 900 34 | 246 LET TS = 180 35 | 247 LET BS = 270 36 | 250 GOTO 400 37 | 38 | 260 LET DX = 100 39 | 265 LET DY = 900 40 | 266 LET TS = 270 41 | 267 LET BS = 360 42 | 270 GOTO 400 43 | 44 | 45 | 400 GOSUB 2000 46 | 420 DRIVE A, SPEEDSTOP 47 | 430 LET DAM = DAMAGE 48 | 440 GOSUB 3000 49 | 450 GOTO 100 50 | 51 | 52 | 53 | 54 | 2000 REM drive to around DX,DY 55 | 2003 GOSUB 2110 : REM get angle to target 56 | 2004 LET AA = A 57 | 2005 DRIVE AA,SPEEDHIGH 58 | 2006 GOSUB 5000 : REM get distance to target 59 | 2007 GOSUB 7000 60 | 2010 IF D < 50 THEN GOTO 2030 61 | 2015 IF D < 100 THEN GOTO 2040 62 | 2020 IF D >= 100 THEN GOTO 2005 63 | 2030 DRIVE AA,SPEEDSTOP 64 | 2035 RETURN 65 | 2040 DRIVE AA, SPEEDMID 66 | 2045 GOTO 2006 67 | 68 | 69 | 2100 REM get angle to target 70 | 2110 LET LX = LOCX 71 | 2115 LET LY = LOCY 72 | 2120 LET A = ATN2 DY-LY, DX-LX 73 | 2125 RETURN 74 | 75 | 3000 REM scan and shoot 76 | 3001 REM from TS to BS 77 | 3010 LET SCANANGLE = TS 78 | 3015 IF DAM <> DAMAGE THEN RETURN 79 | 3020 LET RANGE = SCAN SCANANGLE, 5 80 | 3025 IF RANGE < 700 AND RANGE > 40 THEN GOTO 3050 81 | 3030 LET SCANANGLE = SCANANGLE + 5 82 | 3035 IF SCANANGLE >= BS THEN RETURN 83 | 3040 GOTO 3015 84 | 3050 LET DIST = SCAN SCANANGLE,2 85 | 3051 LET DD = DIST + (DIST - RANGE) 86 | 3060 CANNON SCANANGLE, DD 87 | 3070 GOTO 3015 88 | 89 | 5000 REM calculate distance 90 | 5001 REM DX,DY is the destination 91 | 5002 REM D is the distance 92 | 5005 LET LX = LOCX 93 | 5010 LET LY = LOCY 94 | 5015 LET TX = DX-LX 95 | 5020 LET TY = DY-LY 96 | 5025 LET D = SQR (TX*TX)+(TY*TY) 97 | 5030 RETURN 98 | 99 | 100 | 7000 LET ROAMDIST = SCAN ROAMSCAN, ROAMWIDTH 101 | 7010 IF ROAMDIST > 0 THEN GOTO 7050 102 | 7020 LET ROAMSCAN = ROAMSCAN + ROAMWIDTH 103 | 7022 IF ROAMSCAN > 360 THEN LET ROAMSCAN = ROAMSCAN - 360 104 | 7030 RETURN 105 | 106 | 7050 CANNON ROAMSCAN, ROAMDIST 107 | 7060 GOTO 7030 108 | -------------------------------------------------------------------------------- /robots/nexus.bas: -------------------------------------------------------------------------------- 1 | 1 REM nexus.bas 2 | 2 REM move to a corner and scan and shoot 3 | 4 | 10 LET SPEEDHIGH = 100 5 | 11 LET SPEEDMID = 50 6 | 12 LET SPEEDLOW = 10 7 | 13 LET SPEEDSTOP = 0 8 | 9 | 10 | 100 LET Q = RND 4 11 | 105 IF Q = 0 THEN GOTO 200 12 | 110 IF Q = 1 THEN GOTO 220 13 | 115 IF Q = 2 THEN GOTO 240 14 | 120 IF Q = 3 THEN GOTO 260 15 | 130 GOTO 240 16 | 17 | 200 LET DX = 100 18 | 205 LET DY = 100 19 | 210 GOTO 400 20 | 21 | 220 LET DX = 900 22 | 225 LET DY = 100 23 | 230 GOTO 400 24 | 25 | 240 LET DX = 900 26 | 245 LET DY = 900 27 | 250 GOTO 400 28 | 29 | 260 LET DX = 100 30 | 265 LET DY = 900 31 | 270 GOTO 400 32 | 33 | 34 | 400 GOSUB 2000 35 | 410 IF D > 50 THEN GOTO 400 36 | 420 GOTO 100 37 | 38 | 39 | 40 | 41 | 2000 REM drive to around DX,DY 42 | 2003 GOSUB 2110 : REM get angle to target 43 | 2004 DRIVE A,SPEEDHIGH 44 | 2005 GOSUB 5000 : REM get distance to target 45 | 2010 IF D < 50 THEN GOTO 2030 46 | 2015 IF D < 125 THEN GOTO 2040 47 | 2020 IF D >= 125 THEN GOTO 2005 48 | 2030 DRIVE A,SPEEDSTOP 49 | 2031 GOSUB 3010 50 | 2035 RETURN 51 | 2040 DRIVE A, SPEEDLOW 52 | 2045 GOTO 2005 53 | 54 | 55 | 2100 REM get angle to target 56 | 2110 LET LX = LOCX 57 | 2115 LET LY = LOCY 58 | 2120 LET A = ATN2 DY-LY, DX-LX 59 | 2125 RETURN 60 | 61 | 3000 REM scan and shoot 62 | 3001 REM full 360 63 | 3010 LET SCANANGLE = 0 64 | 3020 LET RANGE = SCAN SCANANGLE, 5 65 | 3025 IF RANGE < 700 AND RANGE > 40 THEN GOTO 3050 66 | 3030 LET SCANANGLE = SCANANGLE + 10 67 | 3035 IF SCANANGLE = 360 THEN RETURN 68 | 3050 CANNON SCANANGLE, RANGE 69 | 3055 GOTO 3020 70 | 71 | 5000 REM calculate distance 72 | 5001 REM DX,DY is the destination 73 | 5002 REM D is the distance 74 | 5005 LET LX = LOCX 75 | 5010 LET LY = LOCY 76 | 5015 LET TX = DX-LX 77 | 5020 LET TY = DY-LY 78 | 5025 LET D = SQR (TX*TX)+(TY*TY) 79 | 5030 RETURN -------------------------------------------------------------------------------- /robots/rabbit-v1.bas: -------------------------------------------------------------------------------- 1 | 1 REM rabbit-v1 moves around battle field randomly. Used as moving target for testing. 2 | 3 | 5 LET HIGHSPEED = 100 4 | 6 LET SLOWSPEED = 25 5 | 6 | 10 REM PICK A NEW LOCATION 7 | 11 LET dx = (RND 800 ) + 100 : REM destination X 8 | 15 LET dy = (RND 800 ) + 100 : REM destination Y 9 | 16 REM PRINT "NEW LOCATION ",dx,dy,"\n" 10 | 11 | 20 LET x = LOCX : REM current location X 12 | 25 LET y = LOCY : REM current location Y 13 | 30 LET a = ATN2 dy-y,dx-x : REM get angle to target 14 | 31 REM PRINT x,y,dx,dy,a,"\n" 15 | 40 DRIVE a,HIGHSPEED 16 | 50 GOSUB 2010 17 | 60 if d > 100 THEN GOTO 50 18 | 70 REM slow down and creep to destination 19 | 80 DRIVE a,SLOWSPEED 20 | 90 GOSUB 2010 21 | 100 if d > 10 THEN GOTO 90 22 | 110 REM Arrived. Shut drive down. 23 | 110 DRIVE a,0 24 | 120 GOTO 10 : REM pick a new destination 25 | 26 | 1000 LET XX = LOCX 27 | 1002 LET YY = LOCY 28 | 1010 IF XX <= dx THEN LET XR = XX/dx*100 29 | 1015 IF YY <= dy THEN LET YR = YY/dx*100 30 | 1020 IF XX > dx THEN LET XR = dx/XX*100 31 | 1025 IF YY > dy THEN LET YR = dx/YY*100 32 | 1027 REM print XR,YR,"\n" 33 | 1050 IF XR > 90 AND YR > 90 THEN RETURN 34 | 1060 GOTO 1000 35 | 36 | 2000 REM Distance routine 37 | 2001 REM Destination is dx,dy 38 | 2010 LET cx = LOCX 39 | 2012 LET cy = LOCY 40 | 2014 LET tx = cx-dx 41 | 2016 LET ty = cy-dy 42 | 2020 LET d = SQR (tx*tx)+(ty*ty) 43 | 2030 RETURN 44 | -------------------------------------------------------------------------------- /robots/rook.bas: -------------------------------------------------------------------------------- 1 | 1 REM rook.bas 2 | 2 REM only move in the X or Y at a time. 3 | 4 | 5 LET T = 0 5 | 6 LET DX = 0 6 | 7 LET DY = 0 7 | 8 LET D = 0 8 | 9 LET DA = 0 9 | 10 LET RSA = 0 : REM Rolling scan angle 10 | 11 LET RWD = 3 : REM Rolling scan width 11 | 12 | 200 T = RND 2 13 | 205 IF T = 0 THEN GOSUB 220 14 | 206 IF T = 1 THEN GOSUB 320 15 | 210 GOSUB 9000 16 | 250 GOTO 200 17 | 18 | 219 REM move in the X plane 19 | 220 LET DX = (RND 800) + 100 20 | 221 LET LX = LOCX 21 | 222 LET LY = LOCY 22 | 223 LET DY = LY 23 | 225 IF LX > DX THEN LET DA = 180 ELSE LET DA = 0 24 | 226 DRIVE DA,100 25 | 230 GOSUB 8000 26 | 235 IF D < 20 THEN GOTO 250 27 | 237 GOSUB 7000 : REM rolling scan and shoot 28 | 240 GOTO 221 29 | 250 DRIVE DA, 0 30 | 260 RETURN 31 | 32 | 319 REM move in the Y plane 33 | 320 LET DY = (RND 800) + 100 34 | 321 LET LX = LOCX 35 | 322 LET LY = LOCY 36 | 323 LET DX = LX 37 | 325 IF LY > DY THEN LET DA = 270 ELSE LET DA = 90 38 | 326 DRIVE DA,100 39 | 330 GOSUB 8000 40 | 335 IF D < 20 THEN GOTO 350 41 | 337 GOSUB 7000 : REM rolling scan and shoot 42 | 340 GOTO 322 43 | 350 DRIVE DA, 0 44 | 360 RETURN 45 | 46 | 47 | 7000 REM scan and shoot while moving to target 48 | 7010 LET RR = SCAN RSA, RWD 49 | 7020 if RR > 0 AND RR < 700 THEN GOTO 7100 50 | 7030 LET RSA = RSA + RWD 51 | 7035 IF RSA > 360 THEN LET RSA = RDA - 360 52 | 7040 RETURN 53 | 7100 CANNON RSA, RR 54 | 7110 RETURN 55 | 56 | 57 | 58 | 8000 REM calculate distance to target 59 | 8010 LET TX = DX - LX 60 | 8015 LET TY = DY - LY 61 | 8020 LET D = SQR (TX*TX)+(TY*TY) 62 | 8025 RETURN 63 | 64 | 65 | 9000 REM scan and shoot 66 | 9010 LET SD = 0 67 | 9011 LET SW = 5 68 | 9020 LET R = SCAN SD,SW 69 | 9030 IF R = 0 THEN GOTO 9060 70 | 9040 IF R > 700 THEN GOTO 9060 71 | 9050 CANNON SD,R 72 | 9051 LET SW = SW - 1 73 | 9052 IF SW = 1 THEN LET SW = 5 74 | 9055 GOTO 9020 75 | 9060 LET SD = SD + 5 76 | 9065 IF SD > 360 THEN RETURN 77 | 9070 GOTO 9020 78 | -------------------------------------------------------------------------------- /robots/sniper-v1.bas: -------------------------------------------------------------------------------- 1 | 1 REM FIND TARGET AND SHOOT 2 | 10 LET SCANDIR = 0 3 | 12 LET SCANWIDTH = 5 4 | 30 LET D = SCAN SCANDIR, SCANWIDTH 5 | 40 IF D >= 50 AND D <= 700 THEN GOSUB 210 6 | 50 LET SCANDIR = SCANDIR + 2 7 | 55 IF SCANDIR > 360 THEN LET SCANDIR = SCANDIR - 360 8 | 56 IF SCANDIR < 0 THEN LET SCANDIR = SCANDIR + 360 9 | 60 GOTO 30 10 | 11 | 200 REM FIRE CANNON 12 | 210 CANNON SCANDIR,D 13 | 220 RETURN 14 | -------------------------------------------------------------------------------- /robots/target.bas: -------------------------------------------------------------------------------- 1 | 10 REM First test program 2 | 20 REM Just Loops forever 3 | 30 GOTO 30 -------------------------------------------------------------------------------- /scan.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | // GetAngle : Return the angle in degrees from x1,y1 to x2,y2 8 | func GetAngle(x1, y1, x2, y2 float64) float64 { 9 | 10 | //a := math.Atan2(y2-y1, x2-x1) * RAD2DEG 11 | //a = math.Abs(math.Mod(a, 360.0)) 12 | // fmt.Printf("\n%5.2f %5.2f %5.2f %5.2f %5.2f\n", x1, y1, x2, y2, a) 13 | //return a 14 | 15 | a := math.Atan2(y2-y1, x2-x1) * RAD2DEG 16 | 17 | if a < 0 { 18 | a += 360 19 | } 20 | if a > 360 { 21 | a -= 360 22 | } 23 | 24 | return a 25 | 26 | } 27 | 28 | // GetDistance : Return the distance between x1,y1 to x2,y2 29 | func GetDistance(x1, y1, x2, y2 float64) float64 { 30 | d := math.Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) 31 | return d 32 | } 33 | 34 | // Scanner : Scan angle with within x,y of both robots. 35 | // Returns distance if robot found or 0 if none found. 36 | func Scanner(angle, width, b1x, b1y, b2x, b2y float64) float64 { 37 | 38 | a := GetAngle(b1x, b1y, b2x, b2y) 39 | d := GetDistance(b1x, b1y, b2x, b2y) 40 | 41 | if width > 10.0 { 42 | width = 10.0 43 | } 44 | 45 | if width < 2.0 { 46 | width = 2.0 47 | } 48 | 49 | widthL := angle - width 50 | widthH := angle + width 51 | 52 | lowadj := widthL + 360.0 53 | highadj := widthH + 360.0 54 | 55 | var botAngleAdj float64 56 | 57 | botAngleAdj = a + 360 58 | if botAngleAdj <= widthH { 59 | botAngleAdj += 360 60 | } 61 | 62 | if botAngleAdj >= lowadj && botAngleAdj <= highadj { 63 | if d < 0 { 64 | d = math.Abs(d) 65 | } 66 | return d 67 | } 68 | 69 | return 0 70 | 71 | } 72 | -------------------------------------------------------------------------------- /testbots/bench.bas: -------------------------------------------------------------------------------- 1 | 2 LET B = 0 2 | 3 | 5 LET I = 1 4 | 10 FOR CC = 1 TO 25 5 | 20 GOSUB 500 6 | 30 NEXT CC 7 | 40 GOTO 2 8 | 9 | 500 GOSUB 1010 10 | 510 LET AA = ATN B 11 | 520 LET D = SCAN AA,10 12 | 530 IF D <> 0 THEN CANNON AA,700 13 | 540 DRIVE,AA,1 14 | 550 LET I = I + 1 15 | 560 RETURN 16 | 17 | 1010 FOR NN = 0 TO I 18 | 1020 LET A=0 19 | 1030 LET B=1 20 | 1040 FOR II=0 TO NN 21 | 1050 LET C=B 22 | 1060 LET B=A+B 23 | 1070 LET A=C 24 | 1080 NEXT II 25 | 1090 REM PRINT B," " 26 | 1100 NEXT NN 27 | 1120 RETURN 28 | -------------------------------------------------------------------------------- /testbots/bench2.bas: -------------------------------------------------------------------------------- 1 | 10 DIM P(1000) 2 | 100 GOSUB 15020 3 | 110 GOTO 100 4 | 9000 LET LLX = LOCX 5 | 9005 LET LLY = LOCY 6 | 9010 LET ANTT = ATN2 DY - LLY, DX - LLX 7 | 9015 RETURN 8 | 9050 LET LX = LOCX 9 | 9055 LET LY = LOCY 10 | 9060 LET TX = DX - LX 11 | 9065 LET TY = DY - LY 12 | 9070 LET DTT = SQR (TX*TX) + (TY*TY) 13 | 9075 RETURN 14 | 9100 LET DX = (RND 700) + 100 15 | 9105 LET DY = (RND 700) + 100 16 | 9110 RETURN 17 | 15020 FOR I=2 TO 1000 18 | 15030 LET P[I]=1 19 | 15040 NEXT I 20 | 15050 LET P[1]=0 21 | 15060 FOR Q=2 TO 32 22 | 15070 IF P[Q]=0 THEN GOTO 15110 23 | 15075 LET QQ = Q * Q 24 | 15080 FOR I = QQ TO 1000 STEP Q 25 | 15090 LET P[I]=0 26 | 15100 NEXT I 27 | 15110 NEXT Q 28 | 15120 REM PRINT "FERTIG" 29 | 15130 REM PAUSE 1000 30 | 15140 LET C=0 31 | 15150 FOR I=1 TO 1000 32 | 15160 IF P[I]=0 THEN GOTO 15240 33 | 15170 IF C<8 THEN GOTO 15200 34 | 15180 REM PRINT 35 | 15190 LET C=0 36 | 15200 LET C=C+1 37 | 15210 REM IF I<10 THEN PRINT " "; 38 | 15220 REM IF I<100 THEN PRINT " "; 39 | 15229 REM PRINT I;" "; 40 | 15230 CANNON I % 360, 700 41 | 15232 LET SY = SCAN I % 360, 10 42 | 15240 NEXT I 43 | 15242 GOSUB 9100 44 | 15245 GOSUB 9000 45 | 15246 DRIVE ANTT, 100 46 | 15247 GOSUB 9050 47 | 15250 IF DTT < 50 THEN DRIVE ANTT, 0 48 | 15250 RETURN 49 | 50 | -------------------------------------------------------------------------------- /testbots/bench3.bas: -------------------------------------------------------------------------------- 1 | 10 DIM P(1000) 2 | 3 | 100 GOSUB 15020 4 | 5 | 120 IF LXX < 200 && LYY < 200 THEN GOSUB 500 6 | 130 IF LXX < 200 && LYY > 800 THEN GOSUB 600 7 | 140 IF LXX > 800 && LYY > 800 THEN GOSUB 700 8 | 150 IF LXX > 800 && LYY < 200 THEN GOSUB 800 9 | 160 GOTO 100 10 | 11 | 12 | 500 LET DX = 100 13 | 505 LET DY = 100 14 | 510 GOSUB 9000 15 | 520 DRIVE ANTT, 50 16 | 530 GOSUB 9050 17 | 540 IF DTT > 50 GOTO 530 18 | 540 DRIVE ANTT, 0 19 | 541 GOSUB 9100 20 | 542 GOSUB 9000 21 | 543 DRIVE ANTT, 100 22 | 550 RETURN 23 | 24 | 600 LET DX = 100 25 | 605 LET DY = 900 26 | 610 GOSUB 9000 27 | 620 DRIVE ANTT, 50 28 | 630 GOSUB 9050 29 | 640 IF DTT > 50 GOTO 530 30 | 640 DRIVE ANTT, 0 31 | 641 GOSUB 9100 32 | 642 GOSUB 9000 33 | 643 DRIVE ANTT, 100 34 | 650 RETURN 35 | 36 | 700 LET DX = 100 37 | 705 LET DY = 900 38 | 710 GOSUB 9000 39 | 720 DRIVE ANTT, 50 40 | 730 GOSUB 9050 41 | 740 IF DTT > 50 GOTO 530 42 | 740 DRIVE ANTT, 0 43 | 741 GOSUB 9100 44 | 742 GOSUB 9000 45 | 743 DRIVE ANTT, 100 46 | 750 RETURN 47 | 48 | 800 LET DX = 900 49 | 805 LET DY = 100 50 | 810 GOSUB 9000 51 | 820 DRIVE ANTT, 50 52 | 830 GOSUB 9050 53 | 840 IF DTT > 50 GOTO 530 54 | 840 DRIVE ANTT, 0 55 | 841 GOSUB 9100 56 | 842 GOSUB 9000 57 | 843 DRIVE ANTT, 100 58 | 850 RETURN 59 | 60 | 61 | 9000 LET LLX = LOCX 62 | 9005 LET LLY = LOCY 63 | 9010 LET ANTT = ATN2 DY - LLY, DX - LLX 64 | 9015 RETURN 65 | 66 | 9050 LET LX = LOCX 67 | 9055 LET LY = LOCY 68 | 9060 LET TX = DX - LX 69 | 9065 LET TY = DY - LY 70 | 9070 LET DTT = SQR (TX*TX) + (TY*TY) 71 | 9075 RETURN 72 | 73 | 74 | 75 | 9100 LET DX = (RND 800) + 100 76 | 9105 LET DY = (RND 800) + 100 77 | 9110 RETURN 78 | 79 | 80 | 15020 FOR I=2 TO 1000 81 | 15030 LET P[I]=1 82 | 15040 NEXT I 83 | 15050 LET P[1]=0 84 | 15060 FOR Q=2 TO 32 85 | 15070 IF P[Q]=0 THEN GOTO 15110 86 | 15075 LET QQ = Q * Q 87 | 15080 FOR I = QQ TO 1000 STEP Q 88 | 15090 LET P[I]=0 89 | 15100 NEXT I 90 | 15110 NEXT Q 91 | 15120 REM PRINT "FERTIG" 92 | 15130 REM PAUSE 1000 93 | 15140 LET C=0 94 | 15150 FOR I=1 TO 1000 95 | 15160 IF P[I]=0 THEN GOTO 15240 96 | 15170 IF C<8 THEN GOTO 15200 97 | 15180 REM PRINT 98 | 15190 LET C=0 99 | 15200 LET C=C+1 100 | 15210 REM IF I<10 THEN PRINT " "; 101 | 15220 REM IF I<100 THEN PRINT " "; 102 | 15240 NEXT I 103 | 15250 RETURN 104 | 105 | -------------------------------------------------------------------------------- /testbots/shooter.bas: -------------------------------------------------------------------------------- 1 | 1 REM pick a spot, run to it, shoot while moving, lead shots 2 | 2 LET DX = 0 3 | 3 LET DY = 0 4 | 4 LET DMG = 0 5 | 5 LET ODMG = 0 6 | 6 LET LX = 0 7 | 7 LET LY = 0 8 | 8 LET DTT = 0 9 | 9 LET ATT = 0 10 | 10 LET RSA = 0 11 | 11 let RSW = 5 12 | 13 | 20 LET TTEAM = TEAM 14 | 21 REM PRINT "TEAM",TTEAM,"\n" 15 | 16 | 100 REM main loop 17 | 110 DMG = DAMAGE 18 | 120 GOSUB 2000 : REM pick spot 19 | 130 GOSUB 2600 : REM get angle 20 | 140 DRIVE ATT, 100 21 | 150 GOSUB 2500 : REM get distance 22 | 160 IF DTT < 50 THEN GOTO 200 23 | 160 IF DMG <> ODMG THEN GOTO 250 24 | 170 GOSUB 2700 25 | 180 GOTO 150 26 | 27 | 200 DRIVE ATT,0 28 | 210 GOTO 120 29 | 30 | 249 REM damage! 31 | 250 LET ODMG = DMG 32 | 260 GOSUB 2000 : REM pick spot 33 | 270 GOSUB 2600 : REM get angle 34 | 275 DRIVE ATT, 100 35 | 280 GOSUB 2500 : REM get distance 36 | 290 IF DTT < 100 THEN G0T0 350 37 | 300 GOSUB 2700 38 | 310 RETURN 39 | 350 DRIVE ATT,0 40 | 360 RETURN 41 | 42 | 2000 REM pick random DX,DY 43 | 2010 LET DX = (RND 700) + 100 44 | 2020 LET DY = (RND 700) + 100 45 | 2030 RETURN 46 | 47 | 2500 REM get distance to target 48 | 2510 LET LX = LOCX 49 | 2515 LET LY = LOCY 50 | 2520 LET TX = DX - LOCX 51 | 2521 LET TY = DY - LY 52 | 2530 LET DTT = SQR (TX*TX) + (TY*TY) 53 | 2540 RETURN 54 | 55 | 2600 REM angle to target 56 | 2610 LET LX = LOCX 57 | 2620 LET LY = LOCY 58 | 2630 LET ATT = ATN2 DY-LY, DX - LX 59 | 2640 RETURN 60 | 61 | 2700 REM scan and shoot 62 | 2710 LET SDTT = SCAN RSA,RSW 63 | 2720 IF SDTT > 0 AND SDTT < 700 THEN GOTO 2800 64 | 2730 LET RSA = RSA + RSW 65 | 2731 IF RSA > 360 THEN LET RSA = RSA - 360 66 | 2732 IF RSA < 0 THEN LET RSA = RSA + 360 67 | 2740 GOSUB 6000 68 | 2750 RETURN 69 | 70 | 2800 LET DTTN = SCAN RSA,RSW 71 | 2810 LET DIF = DTTN + (DTTN - SDTT) 72 | 2820 CANNON RSA, DIF 73 | 2822 GOSUB 7000 74 | 2830 RETURN 75 | 76 | 6000 LET MSG = IN 77 | 6001 IF LEN MSG = 0 THEN RETURN 78 | 6010 LET MSGX = LEFT$ MSG, 3 79 | 6020 LET MSGY = RIGHT$ MSG,3 80 | 6030 LET MX = LOCX 81 | 6040 LET MY = LOCY 82 | 6060 LET MA = ATN2 MSGY-MY,MSGY-MX 83 | 6070 LET MD1 = MSGX-MX 84 | 6080 LET MD2 = MSGY-MY 85 | 6090 LET MD = SQR MD1*MD1+MD2*MD2 86 | 6100 CANNON MA,MD 87 | 6110 RETURN 88 | 89 | 90 | 7000 LET IX = ((COS RSA) * DTTN) + LX 91 | 7012 LET IY = ((SIN RSA) * DTTN) + LY 92 | 7020 LET S1 = STRC$ IX,3 93 | 7030 LET S2 = STRC$ IY,3 94 | 7040 LET S3 = S1 + S2 95 | 7050 OUT S3 96 | 7060 RETURN -------------------------------------------------------------------------------- /token/COPYRIGHT: -------------------------------------------------------------------------------- 1 | gobasic is Copyrighted by Steve Kemp. 2 | Licensed under gpl-v2. Used by permission. 3 | You can find the original 'gobasic' at https://github.com/skx/gobasic 4 | -------------------------------------------------------------------------------- /token/token.go: -------------------------------------------------------------------------------- 1 | // Package token contains the tokens we understand when it comes 2 | // to parsing our BASIC input. 3 | //gobasic is Copyrighted by Steve Kemp. 4 | //Licensed under gpl-v2. Used by permission. 5 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 6 | //package builtin 7 | //gobasic is Copyrighted by Steve Kemp. 8 | //Licensed under gpl-v2. Used by permission. 9 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 10 | 11 | package token 12 | 13 | import ( 14 | "fmt" 15 | "strings" 16 | ) 17 | 18 | // Type is a string 19 | type Type string 20 | 21 | // Token contains a single token. 22 | type Token struct { 23 | // Type holds the type of the token 24 | Type Type 25 | 26 | // Literal holds the literal value. 27 | Literal string 28 | } 29 | 30 | // pre-defined token-types 31 | const ( 32 | // Core 33 | EOF = "EOF" // End of file 34 | NEWLINE = "NEWLINE" // Newlines are kept in our lexer-stream 35 | LINENO = "LINENO" // Line-number of each input. 36 | // Types 37 | IDENT = "IDENT" // Identifier (i.e. variable name) 38 | INT = "INT" // integer literal 39 | STRING = "STRING" // string literal 40 | BUILTIN = "BUILTIN" // builtin-function 41 | // Implemented keywords. 42 | END = "END" 43 | GOSUB = "GOSUB" 44 | GOTO = "GOTO" 45 | INPUT = "INPUT" 46 | LET = "LET" 47 | REM = "REM" 48 | RETURN = "RETURN" 49 | 50 | // Did I mention that for-loops work? :D 51 | FOR = "FOR" 52 | NEXT = "NEXT" 53 | STEP = "STEP" 54 | TO = "TO" 55 | 56 | // And conditionals? 57 | IF = "IF" 58 | THEN = "THEN" 59 | ELSE = "ELSE" 60 | 61 | // Binary operators 62 | AND = "AND" 63 | OR = "OR" 64 | XOR = "XOR" 65 | 66 | // Misc 67 | DEF = "DEF" 68 | DIM = "DIM" 69 | FN = "FN" 70 | READ = "READ" 71 | SWAP = "SWAP" 72 | DATA = "DATA" 73 | 74 | // Woo-operators 75 | ASSIGN = "=" // LET x = 3 76 | ASTERISK = "*" // integer multiplication 77 | COMMA = "," // PRINT 3, 54 78 | MINUS = "-" // integer subtraction 79 | MOD = "%" // integer modulus 80 | PLUS = "+" // integer addition 81 | SLASH = "/" // integer division 82 | POW = "^" // power 83 | COLON = ":" 84 | SEMICOLON = ";" 85 | LBRACKET = "(" 86 | RBRACKET = ")" 87 | LINDEX = "[" 88 | RINDEX = "]" 89 | 90 | // Comparison functions. 91 | GT = ">" 92 | GTEQUALS = ">=" 93 | LT = "<" 94 | LTEQUALS = "<=" 95 | NOTEQUALS = "<>" 96 | ) 97 | 98 | // reversed keywords 99 | var keywords = map[string]Type{ 100 | "and": AND, 101 | "data": DATA, 102 | "dim": DIM, 103 | "def": DEF, 104 | "else": ELSE, 105 | "end": END, 106 | "fn": FN, 107 | "for": FOR, 108 | "gosub": GOSUB, 109 | "goto": GOTO, 110 | "if": IF, 111 | "input": INPUT, 112 | "let": LET, 113 | "next": NEXT, 114 | "or": OR, 115 | "read": READ, 116 | "rem": REM, 117 | "return": RETURN, 118 | "step": STEP, 119 | "swap": SWAP, 120 | "then": THEN, 121 | "to": TO, 122 | "xor": XOR, 123 | } 124 | 125 | // LookupIdentifier used to determine whether identifier is keyword nor not. 126 | // We handle both upper-case and lower-cased keywords, for example both 127 | // "print" and "PRINT" are considered identical. 128 | func LookupIdentifier(identifier string) Type { 129 | id := strings.ToLower(identifier) 130 | if tok, ok := keywords[id]; ok { 131 | return tok 132 | } 133 | return IDENT 134 | } 135 | 136 | // String creates a string-representation of a token 137 | func (t Token) String() string { 138 | 139 | // 140 | // Special-case newline-token doesn't need an embedded newline. 141 | // 142 | lit := t.Literal 143 | if t.Type == NEWLINE { 144 | lit = "\\n" 145 | } 146 | return (fmt.Sprintf("Token{Type:%s Value:%s}", t.Type, lit)) 147 | } 148 | -------------------------------------------------------------------------------- /tokenizer/COPYRIGHT: -------------------------------------------------------------------------------- 1 | gobasic is Copyrighted by Steve Kemp. 2 | Licensed under gpl-v2. Used by permission. 3 | You can find the original 'gobasic' at https://github.com/skx/gobasic 4 | -------------------------------------------------------------------------------- /tokenizer/tokenizer.go: -------------------------------------------------------------------------------- 1 | // Package tokenizer contains the tokenizer we use for parsing BASIC programs. 2 | // 3 | // Given a string containing a complete BASIC program this package allows 4 | // that to be iterated over as a series of tokens. 5 | // 6 | // Our interpeter is intentionally naive, and executes tokens directly, without 7 | // any intermediary representation. 8 | // 9 | //gobasic is Copyrighted by Steve Kemp. 10 | //Licensed under gpl-v2. Used by permission. 11 | //You can find the original 'gobasic' at https://github.com/skx/gobasic 12 | 13 | package tokenizer 14 | 15 | import ( 16 | "basicbots/token" 17 | ) 18 | 19 | // Tokenizer holds our state. 20 | type Tokenizer struct { 21 | // current character position. 22 | position int 23 | 24 | // next character position. 25 | readPosition int 26 | 27 | // current character. 28 | ch rune 29 | 30 | // rune slice of input string. 31 | characters []rune 32 | 33 | // The previous token. 34 | prevToken token.Token 35 | } 36 | 37 | // New returns a Tokenizer instance from the specified string input. 38 | func New(input string) *Tokenizer { 39 | 40 | // 41 | // NOTE: We parse line-numbers by looking for: 42 | // 43 | // 1. NEWLINE 44 | // 2. INT 45 | // 46 | // To ensure that we can find the line-number of the first line 47 | // we also setup a fake "previous" character of a newline. This 48 | // means we don't actually need to prefix our input with such a thing. 49 | // 50 | l := &Tokenizer{characters: []rune(input)} 51 | l.prevToken.Type = token.NEWLINE 52 | l.readChar() 53 | return l 54 | } 55 | 56 | // readChar reads forward one character. 57 | func (l *Tokenizer) readChar() { 58 | if l.readPosition >= len(l.characters) { 59 | l.ch = rune(0) 60 | } else { 61 | l.ch = l.characters[l.readPosition] 62 | } 63 | l.position = l.readPosition 64 | l.readPosition++ 65 | } 66 | 67 | // NextToken reads and returns the next available token, skipping any 68 | // white space which might be present. 69 | func (l *Tokenizer) NextToken() token.Token { 70 | var tok token.Token 71 | l.skipWhitespace() 72 | 73 | switch l.ch { 74 | case rune('='): 75 | tok = newToken(token.ASSIGN, l.ch) 76 | case rune(':'): 77 | tok = newToken(token.COLON, l.ch) 78 | case rune(';'): 79 | tok = newToken(token.SEMICOLON, l.ch) 80 | case rune(','): 81 | tok = newToken(token.COMMA, l.ch) 82 | case rune('+'): 83 | tok = newToken(token.PLUS, l.ch) 84 | case rune('-'): 85 | // -3 is "-3". "3 - 4" is -1. 86 | if isDigit(l.peekChar()) { 87 | // swallow the - 88 | l.readChar() 89 | 90 | // read the number 91 | tok.Literal = l.readNumber() 92 | tok.Type = token.INT 93 | 94 | tok.Literal = "-" + tok.Literal 95 | 96 | } else { 97 | tok = newToken(token.MINUS, l.ch) 98 | } 99 | case rune('/'): 100 | tok = newToken(token.SLASH, l.ch) 101 | case rune('^'): 102 | tok = newToken(token.POW, l.ch) 103 | case rune('%'): 104 | tok = newToken(token.MOD, l.ch) 105 | case rune('*'): 106 | tok = newToken(token.ASTERISK, l.ch) 107 | case rune('('): 108 | tok = newToken(token.LBRACKET, l.ch) 109 | case rune(')'): 110 | tok = newToken(token.RBRACKET, l.ch) 111 | case rune('['): 112 | tok = newToken(token.LINDEX, l.ch) 113 | case rune(']'): 114 | tok = newToken(token.RINDEX, l.ch) 115 | case rune('<'): 116 | if l.peekChar() == rune('>') { 117 | ch := l.ch 118 | l.readChar() 119 | tok = token.Token{Type: token.NOTEQUALS, Literal: string(ch) + string(l.ch)} 120 | } else if l.peekChar() == rune('=') { 121 | ch := l.ch 122 | l.readChar() 123 | tok = token.Token{Type: token.LTEQUALS, Literal: string(ch) + string(l.ch)} 124 | } else { 125 | tok = newToken(token.LT, l.ch) 126 | } 127 | case rune('>'): 128 | if l.peekChar() == rune('=') { 129 | ch := l.ch 130 | l.readChar() 131 | tok = token.Token{Type: token.GTEQUALS, Literal: string(ch) + string(l.ch)} 132 | } else { 133 | tok = newToken(token.GT, l.ch) 134 | } 135 | case rune('"'): 136 | tok.Type = token.STRING 137 | tok.Literal = l.readString() 138 | case rune('\n'): 139 | tok.Type = token.NEWLINE 140 | tok.Literal = "\\n" 141 | case rune(0): 142 | tok.Literal = "" 143 | tok.Type = token.EOF 144 | default: 145 | if isDigit(l.ch) { 146 | tok.Literal = l.readNumber() 147 | tok.Type = token.INT 148 | } else { 149 | tok.Literal = l.readIdentifier() 150 | tok.Type = token.LookupIdentifier(tok.Literal) 151 | } 152 | } 153 | l.readChar() 154 | 155 | // 156 | // Hack: A number that follows a newline is a line-number, 157 | // not an integer. 158 | // 159 | if l.prevToken.Type == token.NEWLINE && tok.Type == token.INT { 160 | tok.Type = token.LINENO 161 | } 162 | 163 | // 164 | // Store the previous token - which is used solely for our 165 | // line-number hack. 166 | // 167 | l.prevToken = tok 168 | 169 | return tok 170 | } 171 | 172 | // newToken is a simple helper for returning a new token. 173 | func newToken(tokenType token.Type, ch rune) token.Token { 174 | return token.Token{Type: tokenType, Literal: string(ch)} 175 | } 176 | 177 | // readIdentifier is designed to read an identifier (name of variable, 178 | // function, etc). 179 | func (l *Tokenizer) readIdentifier() string { 180 | 181 | id := "" 182 | 183 | for isIdentifier(l.peekChar()) { 184 | id += string(l.ch) 185 | l.readChar() 186 | } 187 | id += string(l.ch) 188 | return id 189 | } 190 | 191 | // skip white space 192 | func (l *Tokenizer) skipWhitespace() { 193 | for isWhitespace(l.ch) { 194 | l.readChar() 195 | } 196 | } 197 | 198 | // read a number, note that this only handles integers. 199 | func (l *Tokenizer) readNumber() string { 200 | str := "" 201 | 202 | for isDigit(l.peekChar()) || l.peekChar() == rune('.') { 203 | str += string(l.ch) 204 | l.readChar() 205 | } 206 | str += string(l.ch) 207 | return str 208 | } 209 | 210 | // read a string, handling "\t", "\n", etc. 211 | func (l *Tokenizer) readString() string { 212 | out := "" 213 | 214 | for { 215 | l.readChar() 216 | if l.ch == '"' { 217 | break 218 | } 219 | if l.ch == rune(0) { 220 | break 221 | } 222 | 223 | // 224 | // Handle \n, \r, \t, \", etc. 225 | // 226 | if l.ch == '\\' { 227 | l.readChar() 228 | 229 | if l.ch == rune('n') { 230 | l.ch = '\n' 231 | } 232 | if l.ch == rune('r') { 233 | l.ch = '\r' 234 | } 235 | if l.ch == rune('t') { 236 | l.ch = '\t' 237 | } 238 | if l.ch == rune('"') { 239 | l.ch = '"' 240 | } 241 | if l.ch == rune('\\') { 242 | l.ch = '\\' 243 | } 244 | } 245 | out = out + string(l.ch) 246 | } 247 | 248 | return out 249 | } 250 | 251 | // peek character looks at the next character which is available for consumption 252 | func (l *Tokenizer) peekChar() rune { 253 | if l.readPosition >= len(l.characters) { 254 | return rune(0) 255 | } 256 | return l.characters[l.readPosition] 257 | } 258 | 259 | // determinate ch is identifier or not 260 | func isIdentifier(ch rune) bool { 261 | return !isWhitespace(ch) && !isBrace(ch) && !isOperator(ch) && !isComparison(ch) && !isCompound(ch) && !isBrace(ch) && !isParen(ch) && !isBracket(ch) && !isEmpty(ch) && (ch != rune('\n')) 262 | } 263 | 264 | // is white space: note that a newline is NOT considered whitespace 265 | // as we need that in our evaluator. 266 | func isWhitespace(ch rune) bool { 267 | return ch == rune(' ') || ch == rune('\t') || ch == rune('\r') 268 | } 269 | 270 | // is operators 271 | func isOperator(ch rune) bool { 272 | return ch == rune('+') || ch == rune('-') || ch == rune('/') || ch == rune('*') 273 | } 274 | 275 | // is comparison 276 | func isComparison(ch rune) bool { 277 | return ch == rune('=') || ch == rune('!') || ch == rune('>') || ch == rune('<') 278 | } 279 | 280 | // is compound 281 | func isCompound(ch rune) bool { 282 | return ch == rune(',') || ch == rune(':') || ch == rune('"') || ch == rune(';') 283 | } 284 | 285 | // is brace 286 | func isBrace(ch rune) bool { 287 | return ch == rune('{') || ch == rune('}') 288 | } 289 | 290 | // is bracket 291 | func isBracket(ch rune) bool { 292 | return ch == rune('[') || ch == rune(']') 293 | } 294 | 295 | // is parenthesis 296 | func isParen(ch rune) bool { 297 | return ch == rune('(') || ch == rune(')') 298 | } 299 | 300 | // is empty 301 | func isEmpty(ch rune) bool { 302 | return rune(0) == ch 303 | } 304 | 305 | // is Digit 306 | func isDigit(ch rune) bool { 307 | return rune('0') <= ch && ch <= rune('9') 308 | } 309 | -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | 9 | "go.installDependenciesWhenBuilding":true, 10 | "editor.renderWhitespace": "trailing", 11 | "comments.openPanel": "neverOpen" 12 | } 13 | } --------------------------------------------------------------------------------