My attempt at writing a go (the board game) engine, in Rust
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: implement Board type with stone placement, occupancy checks, and property tests

- Add Board<const W, const H> with get/place/remove/is_empty
- Bounds enforced via Coord compile-time guarantees
- Occupancy check returns GoError::Occupied
- Property tests via proptest for all board operations
- 100% line coverage achieved
- Export Board from lib.rs

+792
+583
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "anyhow" 7 + version = "1.0.102" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 10 + 11 + [[package]] 12 + name = "autocfg" 13 + version = "1.5.0" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 16 + 17 + [[package]] 18 + name = "bit-set" 19 + version = "0.8.0" 20 + source = "registry+https://github.com/rust-lang/crates.io-index" 21 + checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" 22 + dependencies = [ 23 + "bit-vec", 24 + ] 25 + 26 + [[package]] 27 + name = "bit-vec" 28 + version = "0.8.0" 29 + source = "registry+https://github.com/rust-lang/crates.io-index" 30 + checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" 31 + 32 + [[package]] 33 + name = "bitflags" 34 + version = "2.11.1" 35 + source = "registry+https://github.com/rust-lang/crates.io-index" 36 + checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" 37 + 38 + [[package]] 39 + name = "cfg-if" 40 + version = "1.0.4" 41 + source = "registry+https://github.com/rust-lang/crates.io-index" 42 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 43 + 44 + [[package]] 45 + name = "equivalent" 46 + version = "1.0.2" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 49 + 50 + [[package]] 51 + name = "errno" 52 + version = "0.3.14" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 55 + dependencies = [ 56 + "libc", 57 + "windows-sys", 58 + ] 59 + 60 + [[package]] 61 + name = "fastrand" 62 + version = "2.4.1" 63 + source = "registry+https://github.com/rust-lang/crates.io-index" 64 + checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" 65 + 66 + [[package]] 67 + name = "fnv" 68 + version = "1.0.7" 69 + source = "registry+https://github.com/rust-lang/crates.io-index" 70 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 71 + 72 + [[package]] 73 + name = "foldhash" 74 + version = "0.1.5" 75 + source = "registry+https://github.com/rust-lang/crates.io-index" 76 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 77 + 78 + [[package]] 79 + name = "getrandom" 80 + version = "0.3.4" 81 + source = "registry+https://github.com/rust-lang/crates.io-index" 82 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 83 + dependencies = [ 84 + "cfg-if", 85 + "libc", 86 + "r-efi 5.3.0", 87 + "wasip2", 88 + ] 89 + 90 + [[package]] 91 + name = "getrandom" 92 + version = "0.4.2" 93 + source = "registry+https://github.com/rust-lang/crates.io-index" 94 + checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" 95 + dependencies = [ 96 + "cfg-if", 97 + "libc", 98 + "r-efi 6.0.0", 99 + "wasip2", 100 + "wasip3", 101 + ] 102 + 103 + [[package]] 6 104 name = "go-engine" 7 105 version = "0.1.0" 8 106 dependencies = [ 107 + "proptest", 9 108 "thiserror", 10 109 ] 11 110 12 111 [[package]] 112 + name = "hashbrown" 113 + version = "0.15.5" 114 + source = "registry+https://github.com/rust-lang/crates.io-index" 115 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 116 + dependencies = [ 117 + "foldhash", 118 + ] 119 + 120 + [[package]] 121 + name = "hashbrown" 122 + version = "0.17.0" 123 + source = "registry+https://github.com/rust-lang/crates.io-index" 124 + checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" 125 + 126 + [[package]] 127 + name = "heck" 128 + version = "0.5.0" 129 + source = "registry+https://github.com/rust-lang/crates.io-index" 130 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 131 + 132 + [[package]] 133 + name = "id-arena" 134 + version = "2.3.0" 135 + source = "registry+https://github.com/rust-lang/crates.io-index" 136 + checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" 137 + 138 + [[package]] 139 + name = "indexmap" 140 + version = "2.14.0" 141 + source = "registry+https://github.com/rust-lang/crates.io-index" 142 + checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" 143 + dependencies = [ 144 + "equivalent", 145 + "hashbrown 0.17.0", 146 + "serde", 147 + "serde_core", 148 + ] 149 + 150 + [[package]] 151 + name = "itoa" 152 + version = "1.0.18" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 155 + 156 + [[package]] 157 + name = "leb128fmt" 158 + version = "0.1.0" 159 + source = "registry+https://github.com/rust-lang/crates.io-index" 160 + checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 161 + 162 + [[package]] 163 + name = "libc" 164 + version = "0.2.186" 165 + source = "registry+https://github.com/rust-lang/crates.io-index" 166 + checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" 167 + 168 + [[package]] 169 + name = "linux-raw-sys" 170 + version = "0.12.1" 171 + source = "registry+https://github.com/rust-lang/crates.io-index" 172 + checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" 173 + 174 + [[package]] 175 + name = "log" 176 + version = "0.4.29" 177 + source = "registry+https://github.com/rust-lang/crates.io-index" 178 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 179 + 180 + [[package]] 181 + name = "memchr" 182 + version = "2.8.0" 183 + source = "registry+https://github.com/rust-lang/crates.io-index" 184 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 185 + 186 + [[package]] 187 + name = "num-traits" 188 + version = "0.2.19" 189 + source = "registry+https://github.com/rust-lang/crates.io-index" 190 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 191 + dependencies = [ 192 + "autocfg", 193 + ] 194 + 195 + [[package]] 196 + name = "once_cell" 197 + version = "1.21.4" 198 + source = "registry+https://github.com/rust-lang/crates.io-index" 199 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 200 + 201 + [[package]] 202 + name = "ppv-lite86" 203 + version = "0.2.21" 204 + source = "registry+https://github.com/rust-lang/crates.io-index" 205 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 206 + dependencies = [ 207 + "zerocopy", 208 + ] 209 + 210 + [[package]] 211 + name = "prettyplease" 212 + version = "0.2.37" 213 + source = "registry+https://github.com/rust-lang/crates.io-index" 214 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 215 + dependencies = [ 216 + "proc-macro2", 217 + "syn", 218 + ] 219 + 220 + [[package]] 13 221 name = "proc-macro2" 14 222 version = "1.0.106" 15 223 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 19 227 ] 20 228 21 229 [[package]] 230 + name = "proptest" 231 + version = "1.11.0" 232 + source = "registry+https://github.com/rust-lang/crates.io-index" 233 + checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" 234 + dependencies = [ 235 + "bit-set", 236 + "bit-vec", 237 + "bitflags", 238 + "num-traits", 239 + "rand", 240 + "rand_chacha", 241 + "rand_xorshift", 242 + "regex-syntax", 243 + "rusty-fork", 244 + "tempfile", 245 + "unarray", 246 + ] 247 + 248 + [[package]] 249 + name = "quick-error" 250 + version = "1.2.3" 251 + source = "registry+https://github.com/rust-lang/crates.io-index" 252 + checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 253 + 254 + [[package]] 22 255 name = "quote" 23 256 version = "1.0.45" 24 257 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 28 261 ] 29 262 30 263 [[package]] 264 + name = "r-efi" 265 + version = "5.3.0" 266 + source = "registry+https://github.com/rust-lang/crates.io-index" 267 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 268 + 269 + [[package]] 270 + name = "r-efi" 271 + version = "6.0.0" 272 + source = "registry+https://github.com/rust-lang/crates.io-index" 273 + checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" 274 + 275 + [[package]] 276 + name = "rand" 277 + version = "0.9.4" 278 + source = "registry+https://github.com/rust-lang/crates.io-index" 279 + checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" 280 + dependencies = [ 281 + "rand_chacha", 282 + "rand_core", 283 + ] 284 + 285 + [[package]] 286 + name = "rand_chacha" 287 + version = "0.9.0" 288 + source = "registry+https://github.com/rust-lang/crates.io-index" 289 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 290 + dependencies = [ 291 + "ppv-lite86", 292 + "rand_core", 293 + ] 294 + 295 + [[package]] 296 + name = "rand_core" 297 + version = "0.9.5" 298 + source = "registry+https://github.com/rust-lang/crates.io-index" 299 + checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 300 + dependencies = [ 301 + "getrandom 0.3.4", 302 + ] 303 + 304 + [[package]] 305 + name = "rand_xorshift" 306 + version = "0.4.0" 307 + source = "registry+https://github.com/rust-lang/crates.io-index" 308 + checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" 309 + dependencies = [ 310 + "rand_core", 311 + ] 312 + 313 + [[package]] 314 + name = "regex-syntax" 315 + version = "0.8.10" 316 + source = "registry+https://github.com/rust-lang/crates.io-index" 317 + checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" 318 + 319 + [[package]] 320 + name = "rustix" 321 + version = "1.1.4" 322 + source = "registry+https://github.com/rust-lang/crates.io-index" 323 + checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" 324 + dependencies = [ 325 + "bitflags", 326 + "errno", 327 + "libc", 328 + "linux-raw-sys", 329 + "windows-sys", 330 + ] 331 + 332 + [[package]] 333 + name = "rusty-fork" 334 + version = "0.3.1" 335 + source = "registry+https://github.com/rust-lang/crates.io-index" 336 + checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" 337 + dependencies = [ 338 + "fnv", 339 + "quick-error", 340 + "tempfile", 341 + "wait-timeout", 342 + ] 343 + 344 + [[package]] 345 + name = "semver" 346 + version = "1.0.28" 347 + source = "registry+https://github.com/rust-lang/crates.io-index" 348 + checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" 349 + 350 + [[package]] 351 + name = "serde" 352 + version = "1.0.228" 353 + source = "registry+https://github.com/rust-lang/crates.io-index" 354 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 355 + dependencies = [ 356 + "serde_core", 357 + ] 358 + 359 + [[package]] 360 + name = "serde_core" 361 + version = "1.0.228" 362 + source = "registry+https://github.com/rust-lang/crates.io-index" 363 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 364 + dependencies = [ 365 + "serde_derive", 366 + ] 367 + 368 + [[package]] 369 + name = "serde_derive" 370 + version = "1.0.228" 371 + source = "registry+https://github.com/rust-lang/crates.io-index" 372 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 373 + dependencies = [ 374 + "proc-macro2", 375 + "quote", 376 + "syn", 377 + ] 378 + 379 + [[package]] 380 + name = "serde_json" 381 + version = "1.0.149" 382 + source = "registry+https://github.com/rust-lang/crates.io-index" 383 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 384 + dependencies = [ 385 + "itoa", 386 + "memchr", 387 + "serde", 388 + "serde_core", 389 + "zmij", 390 + ] 391 + 392 + [[package]] 31 393 name = "syn" 32 394 version = "2.0.117" 33 395 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 39 401 ] 40 402 41 403 [[package]] 404 + name = "tempfile" 405 + version = "3.27.0" 406 + source = "registry+https://github.com/rust-lang/crates.io-index" 407 + checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" 408 + dependencies = [ 409 + "fastrand", 410 + "getrandom 0.4.2", 411 + "once_cell", 412 + "rustix", 413 + "windows-sys", 414 + ] 415 + 416 + [[package]] 42 417 name = "thiserror" 43 418 version = "2.0.18" 44 419 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 59 434 ] 60 435 61 436 [[package]] 437 + name = "unarray" 438 + version = "0.1.4" 439 + source = "registry+https://github.com/rust-lang/crates.io-index" 440 + checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" 441 + 442 + [[package]] 62 443 name = "unicode-ident" 63 444 version = "1.0.24" 64 445 source = "registry+https://github.com/rust-lang/crates.io-index" 65 446 checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 447 + 448 + [[package]] 449 + name = "unicode-xid" 450 + version = "0.2.6" 451 + source = "registry+https://github.com/rust-lang/crates.io-index" 452 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 453 + 454 + [[package]] 455 + name = "wait-timeout" 456 + version = "0.2.1" 457 + source = "registry+https://github.com/rust-lang/crates.io-index" 458 + checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" 459 + dependencies = [ 460 + "libc", 461 + ] 462 + 463 + [[package]] 464 + name = "wasip2" 465 + version = "1.0.3+wasi-0.2.9" 466 + source = "registry+https://github.com/rust-lang/crates.io-index" 467 + checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" 468 + dependencies = [ 469 + "wit-bindgen 0.57.1", 470 + ] 471 + 472 + [[package]] 473 + name = "wasip3" 474 + version = "0.4.0+wasi-0.3.0-rc-2026-01-06" 475 + source = "registry+https://github.com/rust-lang/crates.io-index" 476 + checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" 477 + dependencies = [ 478 + "wit-bindgen 0.51.0", 479 + ] 480 + 481 + [[package]] 482 + name = "wasm-encoder" 483 + version = "0.244.0" 484 + source = "registry+https://github.com/rust-lang/crates.io-index" 485 + checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" 486 + dependencies = [ 487 + "leb128fmt", 488 + "wasmparser", 489 + ] 490 + 491 + [[package]] 492 + name = "wasm-metadata" 493 + version = "0.244.0" 494 + source = "registry+https://github.com/rust-lang/crates.io-index" 495 + checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" 496 + dependencies = [ 497 + "anyhow", 498 + "indexmap", 499 + "wasm-encoder", 500 + "wasmparser", 501 + ] 502 + 503 + [[package]] 504 + name = "wasmparser" 505 + version = "0.244.0" 506 + source = "registry+https://github.com/rust-lang/crates.io-index" 507 + checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" 508 + dependencies = [ 509 + "bitflags", 510 + "hashbrown 0.15.5", 511 + "indexmap", 512 + "semver", 513 + ] 514 + 515 + [[package]] 516 + name = "windows-link" 517 + version = "0.2.1" 518 + source = "registry+https://github.com/rust-lang/crates.io-index" 519 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 520 + 521 + [[package]] 522 + name = "windows-sys" 523 + version = "0.61.2" 524 + source = "registry+https://github.com/rust-lang/crates.io-index" 525 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 526 + dependencies = [ 527 + "windows-link", 528 + ] 529 + 530 + [[package]] 531 + name = "wit-bindgen" 532 + version = "0.51.0" 533 + source = "registry+https://github.com/rust-lang/crates.io-index" 534 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 535 + dependencies = [ 536 + "wit-bindgen-rust-macro", 537 + ] 538 + 539 + [[package]] 540 + name = "wit-bindgen" 541 + version = "0.57.1" 542 + source = "registry+https://github.com/rust-lang/crates.io-index" 543 + checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" 544 + 545 + [[package]] 546 + name = "wit-bindgen-core" 547 + version = "0.51.0" 548 + source = "registry+https://github.com/rust-lang/crates.io-index" 549 + checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" 550 + dependencies = [ 551 + "anyhow", 552 + "heck", 553 + "wit-parser", 554 + ] 555 + 556 + [[package]] 557 + name = "wit-bindgen-rust" 558 + version = "0.51.0" 559 + source = "registry+https://github.com/rust-lang/crates.io-index" 560 + checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" 561 + dependencies = [ 562 + "anyhow", 563 + "heck", 564 + "indexmap", 565 + "prettyplease", 566 + "syn", 567 + "wasm-metadata", 568 + "wit-bindgen-core", 569 + "wit-component", 570 + ] 571 + 572 + [[package]] 573 + name = "wit-bindgen-rust-macro" 574 + version = "0.51.0" 575 + source = "registry+https://github.com/rust-lang/crates.io-index" 576 + checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" 577 + dependencies = [ 578 + "anyhow", 579 + "prettyplease", 580 + "proc-macro2", 581 + "quote", 582 + "syn", 583 + "wit-bindgen-core", 584 + "wit-bindgen-rust", 585 + ] 586 + 587 + [[package]] 588 + name = "wit-component" 589 + version = "0.244.0" 590 + source = "registry+https://github.com/rust-lang/crates.io-index" 591 + checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" 592 + dependencies = [ 593 + "anyhow", 594 + "bitflags", 595 + "indexmap", 596 + "log", 597 + "serde", 598 + "serde_derive", 599 + "serde_json", 600 + "wasm-encoder", 601 + "wasm-metadata", 602 + "wasmparser", 603 + "wit-parser", 604 + ] 605 + 606 + [[package]] 607 + name = "wit-parser" 608 + version = "0.244.0" 609 + source = "registry+https://github.com/rust-lang/crates.io-index" 610 + checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" 611 + dependencies = [ 612 + "anyhow", 613 + "id-arena", 614 + "indexmap", 615 + "log", 616 + "semver", 617 + "serde", 618 + "serde_derive", 619 + "serde_json", 620 + "unicode-xid", 621 + "wasmparser", 622 + ] 623 + 624 + [[package]] 625 + name = "zerocopy" 626 + version = "0.8.48" 627 + source = "registry+https://github.com/rust-lang/crates.io-index" 628 + checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" 629 + dependencies = [ 630 + "zerocopy-derive", 631 + ] 632 + 633 + [[package]] 634 + name = "zerocopy-derive" 635 + version = "0.8.48" 636 + source = "registry+https://github.com/rust-lang/crates.io-index" 637 + checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" 638 + dependencies = [ 639 + "proc-macro2", 640 + "quote", 641 + "syn", 642 + ] 643 + 644 + [[package]] 645 + name = "zmij" 646 + version = "1.0.21" 647 + source = "registry+https://github.com/rust-lang/crates.io-index" 648 + checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+3
Cargo.toml
··· 5 5 6 6 [dependencies] 7 7 thiserror = "2.0.18" 8 + 9 + [dev-dependencies] 10 + proptest = "1.5"
+194
src/board.rs
··· 1 + use crate::color::Color; 2 + use crate::coord::Coord; 3 + use crate::error::GoError; 4 + 5 + #[derive(Clone, Debug, PartialEq, Eq, Hash)] 6 + pub struct Board<const W: usize, const H: usize> { 7 + stones: Vec<Option<Color>>, 8 + } 9 + 10 + impl<const W: usize, const H: usize> Board<W, H> { 11 + pub fn new() -> Self { 12 + Board { 13 + stones: vec![None; W * H], 14 + } 15 + } 16 + 17 + fn index_of(coord: Coord<W, H>) -> usize { 18 + coord.y() as usize * W + coord.x() as usize 19 + } 20 + 21 + pub fn get(&self, coord: Coord<W, H>) -> Option<Color> { 22 + self.stones[Self::index_of(coord)] 23 + } 24 + 25 + /// Place a stone at the given coordinate. 26 + /// Returns `Err(GoError::Occupied)` if the point is not empty. 27 + pub fn place(&mut self, coord: Coord<W, H>, color: Color) -> Result<(), GoError> { 28 + let idx = Self::index_of(coord); 29 + if self.stones[idx].is_some() { 30 + return Err(GoError::Occupied); 31 + } 32 + self.stones[idx] = Some(color); 33 + Ok(()) 34 + } 35 + 36 + /// Remove a stone from the given coordinate. 37 + pub fn remove(&mut self, coord: Coord<W, H>) { 38 + self.stones[Self::index_of(coord)] = None; 39 + } 40 + 41 + pub fn is_empty(&self, coord: Coord<W, H>) -> bool { 42 + self.get(coord).is_none() 43 + } 44 + } 45 + 46 + impl<const W: usize, const H: usize> Default for Board<W, H> { 47 + fn default() -> Self { 48 + Self::new() 49 + } 50 + } 51 + 52 + #[cfg(test)] 53 + mod tests { 54 + use super::*; 55 + 56 + #[test] 57 + fn new_board_is_empty() { 58 + let board = Board::<3, 3>::new(); 59 + for x in 0..3 { 60 + for y in 0..3 { 61 + let coord: Coord<3, 3> = (x, y).try_into().unwrap(); 62 + assert!(board.is_empty(coord)); 63 + assert_eq!(board.get(coord), None); 64 + } 65 + } 66 + } 67 + 68 + #[test] 69 + fn place_and_get() { 70 + let mut board = Board::<5, 5>::new(); 71 + let coord: Coord<5, 5> = (2, 3).try_into().unwrap(); 72 + 73 + assert!(board.place(coord, Color::Black).is_ok()); 74 + assert_eq!(board.get(coord), Some(Color::Black)); 75 + assert!(!board.is_empty(coord)); 76 + } 77 + 78 + #[test] 79 + fn place_occupied() { 80 + let mut board = Board::<5, 5>::new(); 81 + let coord: Coord<5, 5> = (1, 1).try_into().unwrap(); 82 + 83 + assert!(board.place(coord, Color::Black).is_ok()); 84 + assert_eq!(board.place(coord, Color::White), Err(GoError::Occupied)); 85 + } 86 + 87 + #[test] 88 + fn remove_stone() { 89 + let mut board = Board::<5, 5>::new(); 90 + let coord: Coord<5, 5> = (0, 0).try_into().unwrap(); 91 + 92 + board.place(coord, Color::White).unwrap(); 93 + assert!(!board.is_empty(coord)); 94 + 95 + board.remove(coord); 96 + assert!(board.is_empty(coord)); 97 + assert_eq!(board.get(coord), None); 98 + } 99 + 100 + #[test] 101 + fn multiple_stones() { 102 + let mut board = Board::<9, 9>::new(); 103 + let a: Coord<9, 9> = (0, 0).try_into().unwrap(); 104 + let b: Coord<9, 9> = (8, 8).try_into().unwrap(); 105 + let c: Coord<9, 9> = (4, 4).try_into().unwrap(); 106 + 107 + board.place(a, Color::Black).unwrap(); 108 + board.place(b, Color::White).unwrap(); 109 + board.place(c, Color::Black).unwrap(); 110 + 111 + assert_eq!(board.get(a), Some(Color::Black)); 112 + assert_eq!(board.get(b), Some(Color::White)); 113 + assert_eq!(board.get(c), Some(Color::Black)); 114 + } 115 + 116 + #[test] 117 + fn default_is_empty() { 118 + let board = Board::<5, 5>::default(); 119 + let coord: Coord<5, 5> = (2, 2).try_into().unwrap(); 120 + assert!(board.is_empty(coord)); 121 + } 122 + 123 + mod proptests { 124 + use super::*; 125 + use proptest::prelude::*; 126 + 127 + fn coord_5x5() -> impl Strategy<Value = Coord<5, 5>> { 128 + (0u8..5, 0u8..5).prop_map(|(x, y)| Coord::try_from((x, y)).unwrap()) 129 + } 130 + 131 + fn color() -> impl Strategy<Value = Color> { 132 + proptest::bool::ANY.prop_map(|b| if b { Color::Black } else { Color::White }) 133 + } 134 + 135 + proptest! { 136 + #[test] 137 + fn new_board_is_empty((x, y) in (0u8..5, 0u8..5)) { 138 + let board = Board::<5, 5>::new(); 139 + let coord: Coord<5, 5> = (x, y).try_into().unwrap(); 140 + prop_assert!(board.is_empty(coord)); 141 + prop_assert_eq!(board.get(coord), None); 142 + } 143 + 144 + #[test] 145 + fn place_then_get(coord in coord_5x5(), c in color()) { 146 + let mut board = Board::<5, 5>::new(); 147 + prop_assert!(board.place(coord, c).is_ok()); 148 + prop_assert_eq!(board.get(coord), Some(c)); 149 + prop_assert!(!board.is_empty(coord)); 150 + } 151 + 152 + #[test] 153 + fn place_occupied(coord in coord_5x5()) { 154 + let mut board = Board::<5, 5>::new(); 155 + board.place(coord, Color::Black).unwrap(); 156 + prop_assert_eq!(board.place(coord, Color::White), Err(GoError::Occupied)); 157 + } 158 + 159 + #[test] 160 + fn place_remove_then_empty(coord in coord_5x5(), c in color()) { 161 + let mut board = Board::<5, 5>::new(); 162 + board.place(coord, c).unwrap(); 163 + board.remove(coord); 164 + prop_assert!(board.is_empty(coord)); 165 + prop_assert_eq!(board.get(coord), None); 166 + } 167 + 168 + #[test] 169 + fn independent_coords( 170 + c1 in coord_5x5(), 171 + c2 in coord_5x5(), 172 + col1 in color(), 173 + col2 in color(), 174 + ) { 175 + prop_assume!(c1 != c2); 176 + let mut board = Board::<5, 5>::new(); 177 + board.place(c1, col1).unwrap(); 178 + board.place(c2, col2).unwrap(); 179 + prop_assert_eq!(board.get(c1), Some(col1)); 180 + prop_assert_eq!(board.get(c2), Some(col2)); 181 + } 182 + 183 + #[test] 184 + fn is_empty_iff_get_is_none(coord in coord_5x5(), c in color()) { 185 + let mut board = Board::<5, 5>::new(); 186 + prop_assert_eq!(board.is_empty(coord), board.get(coord).is_none()); 187 + board.place(coord, c).unwrap(); 188 + prop_assert_eq!(board.is_empty(coord), board.get(coord).is_none()); 189 + board.remove(coord); 190 + prop_assert_eq!(board.is_empty(coord), board.get(coord).is_none()); 191 + } 192 + } 193 + } 194 + }
+10
src/coord.rs
··· 218 218 let c: Coord<19, 19> = (18, 18).try_into().unwrap(); 219 219 assert_eq!(c.to_sgf_string(), "ss"); 220 220 } 221 + 222 + #[test] 223 + fn gtp_column_to_x_non_alpha() { 224 + assert_eq!(Coord::<19, 19>::gtp_column_to_x('1'), None); 225 + } 226 + 227 + #[test] 228 + fn x_to_gtp_column_too_large() { 229 + assert_eq!(Coord::<19, 19>::x_to_gtp_column(25), None); 230 + } 221 231 }
+2
src/lib.rs
··· 1 + pub mod board; 1 2 pub mod color; 2 3 pub mod coord; 3 4 pub mod error; 4 5 6 + pub use board::Board; 5 7 pub use color::Color; 6 8 pub use coord::Coord; 7 9 pub use error::GoError;