diff options
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | gamestate.cpp | 323 | ||||
| -rw-r--r-- | util.cpp | 8 |
3 files changed, 252 insertions, 81 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index ca45e19..136dadc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -14,4 +14,6 @@ include_directories( | |||
| 14 | ) | 14 | ) |
| 15 | 15 | ||
| 16 | add_executable(mazeoflife highscore.cpp hslist.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp) | 16 | add_executable(mazeoflife highscore.cpp hslist.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp) |
| 17 | set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 11) | ||
| 18 | set_property(TARGET mazeoflife PROPERTY CXX_STANDARD_REQUIRED ON) | ||
| 17 | target_link_libraries(mazeoflife ${SDL2_LIBRARY} ${SDL2_TTF_LIBRARY} ${SDL2_NET_LIBRARIES}) | 19 | target_link_libraries(mazeoflife ${SDL2_LIBRARY} ${SDL2_TTF_LIBRARY} ${SDL2_NET_LIBRARIES}) |
| diff --git a/gamestate.cpp b/gamestate.cpp index f0de67a..3ff08c4 100644 --- a/gamestate.cpp +++ b/gamestate.cpp | |||
| @@ -6,17 +6,32 @@ | |||
| 6 | #include "titlestate.h" | 6 | #include "titlestate.h" |
| 7 | #include <fstream> | 7 | #include <fstream> |
| 8 | #include "hslist.h" | 8 | #include "hslist.h" |
| 9 | #include <set> | ||
| 10 | #include <bitset> | ||
| 11 | #include <tuple> | ||
| 12 | #include <algorithm> | ||
| 13 | #include <vector> | ||
| 14 | #include <deque> | ||
| 15 | #include <iostream> | ||
| 16 | #include <sstream> | ||
| 9 | 17 | ||
| 10 | class GameBoard { | 18 | class GameBoard { |
| 11 | public: | 19 | public: |
| 12 | GameBoard(int level); | 20 | GameBoard(int level, int playerx, int playery); |
| 13 | void tick(int playerx, int playery); | 21 | void tick(int playerx, int playery); |
| 14 | void render(SDL_Renderer* renderer); | 22 | void render(SDL_Renderer* renderer, int level) const; |
| 15 | bool isObstructed(int x, int y); | 23 | bool isObstructed(int x, int y) const; |
| 24 | bool operator<(const GameBoard& other) const; | ||
| 16 | 25 | ||
| 17 | private: | 26 | private: |
| 18 | int level; | 27 | void initialize(int level); |
| 19 | bool blocks[WIDTH][HEIGHT]; | 28 | int solve(int playerx, int playery) const; |
| 29 | std::string dump() const; | ||
| 30 | |||
| 31 | std::bitset<WIDTH*HEIGHT> blocks; | ||
| 32 | std::bitset<WIDTH*HEIGHT> updateable; | ||
| 33 | int oldx; | ||
| 34 | int oldy; | ||
| 20 | }; | 35 | }; |
| 21 | 36 | ||
| 22 | class LoadGameState : public State { | 37 | class LoadGameState : public State { |
| @@ -65,7 +80,7 @@ void setRendererDeadColor(SDL_Renderer* renderer, int level) | |||
| 65 | } | 80 | } |
| 66 | } | 81 | } |
| 67 | 82 | ||
| 68 | void incrementIfNeighbor(int x, int y, bool temp[WIDTH][HEIGHT], int* tick, int playerx, int playery) | 83 | void incrementIfNeighbor(int x, int y, std::bitset<WIDTH*HEIGHT>& temp, int* tick, int playerx, int playery) |
| 69 | { | 84 | { |
| 70 | int nx = x; | 85 | int nx = x; |
| 71 | int ny = y; | 86 | int ny = y; |
| @@ -74,7 +89,7 @@ void incrementIfNeighbor(int x, int y, bool temp[WIDTH][HEIGHT], int* tick, int | |||
| 74 | 89 | ||
| 75 | if (!((nx!=x)&&(ny!=y))) | 90 | if (!((nx!=x)&&(ny!=y))) |
| 76 | { | 91 | { |
| 77 | if ((temp[x][y])||((playerx==x)&&(playery==y))||((x==15)&&(y==15))) | 92 | if ((temp[x+y*WIDTH])||((playerx==x)&&(playery==y))||((x==15)&&(y==15))) |
| 78 | { | 93 | { |
| 79 | ++*tick; | 94 | ++*tick; |
| 80 | } | 95 | } |
| @@ -128,11 +143,7 @@ State* LoadGameState::operator() (SDL_Window* window, SDL_Renderer* renderer) | |||
| 128 | SDL_RenderPresent(renderer); | 143 | SDL_RenderPresent(renderer); |
| 129 | 144 | ||
| 130 | // Do 50 gens of Conway | 145 | // Do 50 gens of Conway |
| 131 | GameBoard* board = new GameBoard(level); | 146 | GameBoard* board = new GameBoard(level, playerx, playery); |
| 132 | for (int i=0; i<50; i++) | ||
| 133 | { | ||
| 134 | board->tick(playerx, playery); | ||
| 135 | } | ||
| 136 | 147 | ||
| 137 | // Wait a bit | 148 | // Wait a bit |
| 138 | SDL_Delay(500); | 149 | SDL_Delay(500); |
| @@ -159,7 +170,7 @@ State* PlayGameState::operator() (SDL_Window* window, SDL_Renderer* renderer) | |||
| 159 | board->tick(playerx, playery); | 170 | board->tick(playerx, playery); |
| 160 | 171 | ||
| 161 | // Paint board | 172 | // Paint board |
| 162 | board->render(renderer); | 173 | board->render(renderer, level); |
| 163 | 174 | ||
| 164 | // Paint event | 175 | // Paint event |
| 165 | SDL_Rect block; | 176 | SDL_Rect block; |
| @@ -292,90 +303,103 @@ bool PlayGameState::move(int x, int y) | |||
| 292 | return false; | 303 | return false; |
| 293 | } | 304 | } |
| 294 | 305 | ||
| 295 | GameBoard::GameBoard(int m_level) | 306 | GameBoard::GameBoard(int level, int playerx, int playery) |
| 296 | { | 307 | { |
| 297 | level = m_level; | 308 | for (;;) |
| 298 | 309 | { | |
| 299 | for (int y=0; y<HEIGHT; y++) | 310 | initialize(level); |
| 300 | { | 311 | updateable.set(); |
| 301 | for (int x=0; x<WIDTH; x++) | 312 | oldx = playerx; |
| 302 | { | 313 | oldy = playery; |
| 303 | blocks[x][y] = false; | 314 | |
| 304 | 315 | for (int i=0; i<50; i++) | |
| 305 | switch (level/10+1) | 316 | { |
| 306 | { | 317 | tick(playerx, playery); |
| 307 | case 1: | 318 | } |
| 308 | if ((x>13)&&(x<17)&&(y>13)&&(y<17)) | 319 | |
| 309 | { | 320 | int states = solve(playerx, playery); |
| 310 | blocks[x][y] = rand() % 2; | 321 | if (states != 0) |
| 311 | } | 322 | { |
| 312 | break; | 323 | break; |
| 313 | case 2: | 324 | } else { |
| 314 | case 3: | 325 | std::cout << "Impossible board: " << playerx << "," << playery << "," << dump() << std::endl; |
| 315 | if ((x>12)&&(x<18)&&(y>12)&&(y<18)) | 326 | } |
| 316 | { | 327 | } |
| 317 | blocks[x][y] = rand() % 2; | ||
| 318 | } | ||
| 319 | break; | ||
| 320 | case 4: | ||
| 321 | case 5: | ||
| 322 | if ((x>11)&&(x<19)&&(y>11)&&(y<19)) | ||
| 323 | { | ||
| 324 | blocks[x][y] = rand() % 2; | ||
| 325 | } | ||
| 326 | break; | ||
| 327 | default: | ||
| 328 | blocks[x][y] = rand() % 2; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | blocks[15][15] = false; | ||
| 334 | } | 328 | } |
| 335 | 329 | ||
| 336 | void GameBoard::tick(int playerx, int playery) | 330 | void GameBoard::tick(int playerx, int playery) |
| 337 | { | 331 | { |
| 338 | bool temp[WIDTH][HEIGHT]; | 332 | std::bitset<WIDTH*HEIGHT> temp {blocks}; |
| 339 | int x,y; | 333 | std::bitset<WIDTH*HEIGHT> tempdateable {updateable}; |
| 340 | for (x=0;x<WIDTH;x++) | 334 | if ((playerx != oldx) || (playery != oldy)) |
| 341 | { | 335 | { |
| 342 | for (y=0;y<HEIGHT;y++) | 336 | for (int dy = -1; dy <= 1; dy++) |
| 343 | { | 337 | { |
| 344 | temp[x][y] = blocks[x][y]; | 338 | for (int dx = -1; dx <=1; dx++) |
| 345 | } | 339 | { |
| 346 | } | 340 | int tdx = oldx+dx; |
| 341 | int tdy = oldy+dy; | ||
| 342 | wrap(&tdx, &tdy); | ||
| 343 | tempdateable.set(tdx+tdy*WIDTH); | ||
| 344 | |||
| 345 | tdx = playerx+dx; | ||
| 346 | tdy = playery+dy; | ||
| 347 | wrap(&tdx, &tdy); | ||
| 348 | tempdateable.set(tdx+tdy*WIDTH); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | oldx = playerx; | ||
| 354 | oldy = playery; | ||
| 355 | |||
| 356 | updateable.reset(); | ||
| 347 | 357 | ||
| 348 | for (x=0;x<WIDTH;x++) | 358 | for (int y=0;y<HEIGHT;y++) |
| 349 | { | 359 | { |
| 350 | for (y=0;y<HEIGHT;y++) | 360 | for (int x=0;x<WIDTH;x++) |
| 351 | { | 361 | { |
| 352 | if ((x==15)&&(y==15)) | 362 | if (((x==15)&&(y==15)) || (!tempdateable[x+y*WIDTH])) |
| 353 | { | 363 | { |
| 354 | continue; | 364 | continue; |
| 355 | } | 365 | } |
| 356 | 366 | ||
| 357 | int neighbors = 0; | 367 | int neighbors = 0; |
| 358 | 368 | ||
| 359 | incrementIfNeighbor(x-1,y-1,temp,&neighbors, playerx, playery); | 369 | incrementIfNeighbor(x-1, y-1, temp, &neighbors, playerx, playery); |
| 360 | incrementIfNeighbor(x-1,y,temp,&neighbors, playerx, playery); | 370 | incrementIfNeighbor(x-1, y , temp, &neighbors, playerx, playery); |
| 361 | incrementIfNeighbor(x-1,y+1,temp,&neighbors, playerx, playery); | 371 | incrementIfNeighbor(x-1, y+1, temp, &neighbors, playerx, playery); |
| 362 | incrementIfNeighbor(x,y-1,temp,&neighbors, playerx, playery); | 372 | incrementIfNeighbor(x , y-1, temp, &neighbors, playerx, playery); |
| 363 | incrementIfNeighbor(x,y+1,temp,&neighbors, playerx, playery); | 373 | incrementIfNeighbor(x , y+1, temp, &neighbors, playerx, playery); |
| 364 | incrementIfNeighbor(x+1,y-1,temp,&neighbors, playerx, playery); | 374 | incrementIfNeighbor(x+1, y-1, temp, &neighbors, playerx, playery); |
| 365 | incrementIfNeighbor(x+1,y,temp,&neighbors, playerx, playery); | 375 | incrementIfNeighbor(x+1, y , temp, &neighbors, playerx, playery); |
| 366 | incrementIfNeighbor(x+1,y+1,temp,&neighbors, playerx, playery); | 376 | incrementIfNeighbor(x+1, y+1, temp, &neighbors, playerx, playery); |
| 367 | 377 | ||
| 368 | if (temp[x][y]) | 378 | if (temp[x+y*WIDTH]) |
| 369 | { | 379 | { |
| 370 | blocks[x][y] = ((neighbors >= 1) && (neighbors <= 4)); | 380 | blocks[x+y*WIDTH] = ((neighbors >= 1) && (neighbors <= 4)); |
| 371 | } else { | 381 | } else { |
| 372 | blocks[x][y] = (neighbors == 3); | 382 | blocks[x+y*WIDTH] = (neighbors == 3); |
| 373 | } | 383 | } |
| 384 | |||
| 385 | if (temp[x+y*WIDTH] != blocks[x+y*WIDTH]) | ||
| 386 | { | ||
| 387 | for (int dy = -1; dy <= 1; dy++) | ||
| 388 | { | ||
| 389 | for (int dx = -1; dx <=1; dx++) | ||
| 390 | { | ||
| 391 | int tdx = x+dx; | ||
| 392 | int tdy = y+dy; | ||
| 393 | wrap(&tdx, &tdy); | ||
| 394 | updateable.set(tdx+tdy*WIDTH); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | } | ||
| 374 | } | 398 | } |
| 375 | } | 399 | } |
| 376 | } | 400 | } |
| 377 | 401 | ||
| 378 | void GameBoard::render(SDL_Renderer* renderer) | 402 | void GameBoard::render(SDL_Renderer* renderer, int level) const |
| 379 | { | 403 | { |
| 380 | SDL_Rect block; | 404 | SDL_Rect block; |
| 381 | block.w = 16; | 405 | block.w = 16; |
| @@ -388,7 +412,7 @@ void GameBoard::render(SDL_Renderer* renderer) | |||
| 388 | block.x = x*16; | 412 | block.x = x*16; |
| 389 | block.y = y*16; | 413 | block.y = y*16; |
| 390 | 414 | ||
| 391 | if (blocks[x][y]) | 415 | if (blocks[x+y*WIDTH]) |
| 392 | { | 416 | { |
| 393 | setRendererAliveColor(renderer, level); | 417 | setRendererAliveColor(renderer, level); |
| 394 | } else { | 418 | } else { |
| @@ -400,7 +424,150 @@ void GameBoard::render(SDL_Renderer* renderer) | |||
| 400 | } | 424 | } |
| 401 | } | 425 | } |
| 402 | 426 | ||
| 403 | bool GameBoard::isObstructed(int x, int y) | 427 | bool GameBoard::isObstructed(int x, int y) const |
| 428 | { | ||
| 429 | return blocks[x+y*WIDTH]; | ||
| 430 | } | ||
| 431 | |||
| 432 | void GameBoard::initialize(int level) | ||
| 433 | { | ||
| 434 | for (int y=0; y<HEIGHT; y++) | ||
| 435 | { | ||
| 436 | for (int x=0; x<WIDTH; x++) | ||
| 437 | { | ||
| 438 | blocks[x+y*WIDTH] = false; | ||
| 439 | |||
| 440 | switch (level/10+1) | ||
| 441 | { | ||
| 442 | case 1: | ||
| 443 | if ((x>13)&&(x<17)&&(y>13)&&(y<17)) | ||
| 444 | { | ||
| 445 | blocks[x+y*WIDTH] = rand() % 2; | ||
| 446 | } | ||
| 447 | break; | ||
| 448 | case 2: | ||
| 449 | case 3: | ||
| 450 | if ((x>12)&&(x<18)&&(y>12)&&(y<18)) | ||
| 451 | { | ||
| 452 | blocks[x+y*WIDTH] = rand() % 2; | ||
| 453 | } | ||
| 454 | break; | ||
| 455 | case 4: | ||
| 456 | case 5: | ||
| 457 | if ((x>11)&&(x<19)&&(y>11)&&(y<19)) | ||
| 458 | { | ||
| 459 | blocks[x+y*WIDTH] = rand() % 2; | ||
| 460 | } | ||
| 461 | break; | ||
| 462 | default: | ||
| 463 | blocks[x+y*WIDTH] = rand() % 2; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | } | ||
| 467 | |||
| 468 | blocks[15+15*WIDTH] = false; | ||
| 469 | } | ||
| 470 | |||
| 471 | bool GameBoard::operator<(const GameBoard& other) const | ||
| 472 | { | ||
| 473 | for (int i = WIDTH*HEIGHT-1; i >= 0; i--) | ||
| 474 | { | ||
| 475 | if (blocks[i] ^ other.blocks[i]) | ||
| 476 | { | ||
| 477 | return other.blocks[i]; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | return false; | ||
| 482 | } | ||
| 483 | |||
| 484 | int GameBoard::solve(int playerx, int playery) const | ||
| 485 | { | ||
| 486 | std::deque<std::tuple<int, int, GameBoard, int>> search; | ||
| 487 | std::set<std::tuple<int, int, GameBoard>> done; | ||
| 488 | search.push_front(std::make_tuple(playerx, playery, *this, 0)); | ||
| 489 | |||
| 490 | bool exists = false; | ||
| 491 | while (!search.empty()) | ||
| 492 | { | ||
| 493 | auto cur = search.front(); | ||
| 494 | search.pop_front(); | ||
| 495 | |||
| 496 | int cpx = std::get<0>(cur); | ||
| 497 | int cpy = std::get<1>(cur); | ||
| 498 | GameBoard& cbr = std::get<2>(cur); | ||
| 499 | int cns = std::get<3>(cur); | ||
| 500 | |||
| 501 | auto cdn = std::make_tuple(cpx, cpy, cbr); | ||
| 502 | done.insert(cdn); | ||
| 503 | |||
| 504 | if (((cpx == 15) && ((cpy == 14) || (cpy == 16))) || ((cpy == 15) && ((cpx == 14) || (cpx == 16)))) | ||
| 505 | { | ||
| 506 | exists = true; | ||
| 507 | break; | ||
| 508 | } | ||
| 509 | |||
| 510 | if (cns >= 100) | ||
| 511 | { | ||
| 512 | continue; | ||
| 513 | } | ||
| 514 | |||
| 515 | GameBoard immnext {cbr}; | ||
| 516 | immnext.tick(cpx, cpy); | ||
| 517 | if (immnext.blocks != cbr.blocks) | ||
| 518 | { | ||
| 519 | if (done.count(std::make_tuple(cpx, cpy, immnext)) == 0) | ||
| 520 | { | ||
| 521 | search.push_front(std::make_tuple(cpx, cpy, immnext, cns)); | ||
| 522 | |||
| 523 | continue; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | std::vector<std::pair<int, int>> dirchanges {{cpx-1,cpy}, {cpx,cpy-1}, {cpx+1,cpy}, {cpx,cpy+1}}; | ||
| 528 | std::sort(std::begin(dirchanges), std::end(dirchanges), [] (const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) { | ||
| 529 | int lhd = sqrt(pow(15 - lhs.first, 2.0) + pow(15 - lhs.second, 2.0)); | ||
| 530 | int rhd = sqrt(pow(15 - rhs.first, 2.0) + pow(15 - rhs.second, 2.0)); | ||
| 531 | |||
| 532 | return lhd > rhd; | ||
| 533 | }); | ||
| 534 | |||
| 535 | for (auto& dirchange : dirchanges) | ||
| 536 | { | ||
| 537 | GameBoard next {cbr}; | ||
| 538 | int npx = cpx + dirchange.first; | ||
| 539 | int npy = cpy + dirchange.second; | ||
| 540 | wrap(&npx, &npy); | ||
| 541 | |||
| 542 | if (!next.isObstructed(npx, npy)) | ||
| 543 | { | ||
| 544 | next.tick(npx, npy); | ||
| 545 | |||
| 546 | if (done.count(std::make_tuple(npx, npy, next)) == 0) | ||
| 547 | { | ||
| 548 | search.push_front(std::make_tuple(npx, npy, next, cns+1)); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | } | ||
| 552 | } | ||
| 553 | |||
| 554 | if (exists) | ||
| 555 | { | ||
| 556 | return done.size(); | ||
| 557 | } else { | ||
| 558 | return 0; | ||
| 559 | } | ||
| 560 | } | ||
| 561 | |||
| 562 | std::string GameBoard::dump() const | ||
| 404 | { | 563 | { |
| 405 | return blocks[x][y]; | 564 | std::stringstream output; |
| 565 | output << std::hex; | ||
| 566 | for (int i=0; i<WIDTH*HEIGHT/4; i++) | ||
| 567 | { | ||
| 568 | int chunk = (8 * blocks[i*4]) + (4 * blocks[i*4+1]) + (2 * blocks[i*4+2]) + blocks[i*4+3]; | ||
| 569 | output << chunk; | ||
| 570 | } | ||
| 571 | |||
| 572 | return output.str(); | ||
| 406 | } | 573 | } |
| diff --git a/util.cpp b/util.cpp index b021146..868161f 100644 --- a/util.cpp +++ b/util.cpp | |||
| @@ -7,12 +7,14 @@ void wrap(int* x, int* y) | |||
| 7 | if (*x < 0) | 7 | if (*x < 0) |
| 8 | { | 8 | { |
| 9 | *x = WIDTH-(0-*x); | 9 | *x = WIDTH-(0-*x); |
| 10 | } else if (*y < 0) | ||
| 11 | { | ||
| 12 | *y = HEIGHT-(0-*y); | ||
| 13 | } else if (*x >= WIDTH) | 10 | } else if (*x >= WIDTH) |
| 14 | { | 11 | { |
| 15 | *x = *x-WIDTH; | 12 | *x = *x-WIDTH; |
| 13 | } | ||
| 14 | |||
| 15 | if (*y < 0) | ||
| 16 | { | ||
| 17 | *y = HEIGHT-(0-*y); | ||
| 16 | } else if (*y >= HEIGHT) | 18 | } else if (*y >= HEIGHT) |
| 17 | { | 19 | { |
| 18 | *y = *y-HEIGHT; | 20 | *y = *y-HEIGHT; |
