117
									
								
								usermods/TetrisAI_v2/gridbw.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								usermods/TetrisAI_v2/gridbw.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : gridbw.h | ||||
|   * @brief          : contains the tetris grid as binary so black and white version | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2023 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __GRIDBW_H__ | ||||
| #define __GRIDBW_H__ | ||||
|  | ||||
| #include <iterator> | ||||
| #include <vector> | ||||
| #include "pieces.h" | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| class GridBW | ||||
| { | ||||
| private: | ||||
| public: | ||||
|     uint8_t width; | ||||
|     uint8_t height; | ||||
|     std::vector<uint32_t> pixels; | ||||
|  | ||||
|     GridBW(uint8_t width, uint8_t height): | ||||
|         width(width), | ||||
|         height(height), | ||||
|         pixels(height) | ||||
|     { | ||||
|         if (width > 32) | ||||
|         { | ||||
|             throw std::invalid_argument("maximal width is 32"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void placePiece(Piece* piece, uint8_t x, uint8_t y) | ||||
|     { | ||||
|         for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++) | ||||
|         { | ||||
|             pixels[y + (row - (4 - piece->getRotation().height))] |= piece->getGridRow(x, row, width); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void erasePiece(Piece* piece, uint8_t x, uint8_t y) | ||||
|     { | ||||
|         for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++) | ||||
|         { | ||||
|             pixels[y + (row - (4 - piece->getRotation().height))] &= ~piece->getGridRow(x, row, width); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bool noCollision(Piece* piece, uint8_t x, uint8_t y) | ||||
|     { | ||||
|         //if it touches a wall it is a collision | ||||
|         if (x > (this->width - piece->getRotation().width) || y > this->height - piece->getRotation().height) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++) | ||||
|         { | ||||
|             if (piece->getGridRow(x, row, width) & pixels[y + (row - (4 - piece->getRotation().height))]) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     void findLandingPosition(Piece* piece) | ||||
|     { | ||||
|         // move down until the piece bumps into some occupied pixels or the 'wall' | ||||
|         while (noCollision(piece, piece->x, piece->landingY)) | ||||
|         { | ||||
|             piece->landingY++; | ||||
|         } | ||||
|  | ||||
|         //at this point the positon is 'in the wall' or 'over some occupied pixel' | ||||
|         //so the previous position was the last correct one (clamped to 0 as minimum). | ||||
|         piece->landingY = piece->landingY > 0 ? piece->landingY - 1 : 0; | ||||
|     } | ||||
|  | ||||
|     void cleanupFullLines() | ||||
|     { | ||||
|         uint8_t offset = 0; | ||||
|  | ||||
|         //from "height - 1" to "0", so from bottom row to top | ||||
|         for (uint8_t row = height; row-- > 0; ) | ||||
|         { | ||||
|             //full line? | ||||
|             if (isLineFull(row)) | ||||
|             { | ||||
|                 offset++; | ||||
|                 pixels[row] = 0x0; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (offset > 0) | ||||
|             { | ||||
|                 pixels[row + offset] = pixels[row]; | ||||
|                 pixels[row] = 0x0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bool isLineFull(uint8_t y) | ||||
|     { | ||||
|         return pixels[y] == (uint32_t)((1 << width) - 1); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __GRIDBW_H__ */ | ||||
							
								
								
									
										132
									
								
								usermods/TetrisAI_v2/gridcolor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								usermods/TetrisAI_v2/gridcolor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : gridcolor.h | ||||
|   * @brief          : contains the tetris grid as 8bit indexed color version | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2023 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __GRIDCOLOR_H__ | ||||
| #define __GRIDCOLOR_H__ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <vector> | ||||
| #include "gridbw.h" | ||||
| #include "gridcolor.h" | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| class GridColor | ||||
| { | ||||
| private: | ||||
| public: | ||||
|     uint8_t width; | ||||
|     uint8_t height; | ||||
|     GridBW gridBW; | ||||
|     std::vector<uint8_t> pixels; | ||||
|  | ||||
|     GridColor(uint8_t width, uint8_t height): | ||||
|         width(width), | ||||
|         height(height), | ||||
|         gridBW(width, height), | ||||
|         pixels(width* height) | ||||
|     {} | ||||
|  | ||||
|     void clear() | ||||
|     { | ||||
|         for (uint8_t y = 0; y < height; y++) | ||||
|         { | ||||
|             gridBW.pixels[y] = 0x0; | ||||
|             for (int8_t x = 0; x < width; x++) | ||||
|             { | ||||
|                 *getPixel(x, y) = 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void placePiece(Piece* piece, uint8_t x, uint8_t y) | ||||
|     { | ||||
|         for (uint8_t pieceY = 0; pieceY < piece->getRotation().height; pieceY++) | ||||
|         { | ||||
|             for (uint8_t pieceX = 0; pieceX < piece->getRotation().width; pieceX++) | ||||
|             { | ||||
|                 if (piece->getPixel(pieceX, pieceY)) | ||||
|                 { | ||||
|                     *getPixel(x + pieceX, y + pieceY) = piece->pieceData->colorIndex; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void erasePiece(Piece* piece, uint8_t x, uint8_t y) | ||||
|     { | ||||
|         for (uint8_t pieceY = 0; pieceY < piece->getRotation().height; pieceY++) | ||||
|         { | ||||
|             for (uint8_t pieceX = 0; pieceX < piece->getRotation().width; pieceX++) | ||||
|             { | ||||
|                 if (piece->getPixel(pieceX, pieceY)) | ||||
|                 { | ||||
|                     *getPixel(x + pieceX, y + pieceY) = 0; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void cleanupFullLines() | ||||
|     { | ||||
|         uint8_t offset = 0; | ||||
|         //from "height - 1" to "0", so from bottom row to top | ||||
|         for (uint8_t y = height; y-- > 0; ) | ||||
|         { | ||||
|             if (gridBW.isLineFull(y)) | ||||
|             { | ||||
|                 offset++; | ||||
|                 for (uint8_t x = 0; x < width; x++) | ||||
|                 { | ||||
|                     pixels[y * width + x] = 0; | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (offset > 0) | ||||
|             { | ||||
|                 if (gridBW.pixels[y]) | ||||
|                 { | ||||
|                     for (uint8_t x = 0; x < width; x++) | ||||
|                     { | ||||
|                         pixels[(y + offset) * width + x] = pixels[y * width + x]; | ||||
|                         pixels[y * width + x] = 0; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         gridBW.cleanupFullLines(); | ||||
|     } | ||||
|  | ||||
|     uint8_t* getPixel(uint8_t x, uint8_t y) | ||||
|     { | ||||
|         return &pixels[y * width + x]; | ||||
|     } | ||||
|  | ||||
|     void sync() | ||||
|     { | ||||
|         for (uint8_t y = 0; y < height; y++) | ||||
|         { | ||||
|             gridBW.pixels[y] = 0x0; | ||||
|             for (int8_t x = 0; x < width; x++) | ||||
|             { | ||||
|                 gridBW.pixels[y] <<= 1; | ||||
|                 if (*getPixel(x, y) != 0) | ||||
|                 { | ||||
|                     gridBW.pixels[y] |= 0x1; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __GRIDCOLOR_H__ */ | ||||
							
								
								
									
										184
									
								
								usermods/TetrisAI_v2/pieces.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								usermods/TetrisAI_v2/pieces.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : pieces.h | ||||
|   * @brief          : contains the tetris pieces with their colors indecies | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2022 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __PIECES_H__ | ||||
| #define __PIECES_H__ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <bitset> | ||||
| #include <cstddef> | ||||
| #include <cassert> | ||||
| #include <iostream> | ||||
|  | ||||
| #define numPieces 7 | ||||
|  | ||||
| struct PieceRotation | ||||
| { | ||||
|     uint8_t width; | ||||
|     uint8_t height; | ||||
|     uint16_t rows; | ||||
| }; | ||||
|  | ||||
| struct PieceData | ||||
| { | ||||
|     uint8_t rotCount; | ||||
|     uint8_t colorIndex; | ||||
|     PieceRotation rotations[4]; | ||||
| }; | ||||
|  | ||||
| PieceData piecesData[numPieces] = { | ||||
|     // I | ||||
|     { | ||||
|             2, | ||||
|             1, | ||||
|             { | ||||
|                 { 1, 4, 0b0001000100010001}, | ||||
|                 { 4, 1, 0b0000000000001111} | ||||
|             } | ||||
|     }, | ||||
|     // O | ||||
|     { | ||||
|             1, | ||||
|             2, | ||||
|             { | ||||
|                 { 2, 2, 0b0000000000110011} | ||||
|             } | ||||
|     }, | ||||
|     // Z | ||||
|     { | ||||
|             2, | ||||
|             3, | ||||
|             { | ||||
|                 { 3, 2, 0b0000000001100011}, | ||||
|                 { 2, 3, 0b0000000100110010} | ||||
|             } | ||||
|     }, | ||||
|     // S | ||||
|     { | ||||
|             2, | ||||
|             4, | ||||
|             { | ||||
|                 { 3, 2, 0b0000000000110110}, | ||||
|                 { 2, 3, 0b0000001000110001} | ||||
|             } | ||||
|     }, | ||||
|     // L | ||||
|     { | ||||
|             4, | ||||
|             5, | ||||
|             { | ||||
|                 { 2, 3, 0b0000001000100011}, | ||||
|                 { 3, 2, 0b0000000001110100}, | ||||
|                 { 2, 3, 0b0000001100010001}, | ||||
|                 { 3, 2, 0b0000000000010111} | ||||
|             } | ||||
|     }, | ||||
|     // J | ||||
|     { | ||||
|             4, | ||||
|             6, | ||||
|             { | ||||
|                 { 2, 3, 0b0000000100010011}, | ||||
|                 { 3, 2, 0b0000000001000111}, | ||||
|                 { 2, 3, 0b0000001100100010}, | ||||
|                 { 3, 2, 0b0000000001110001} | ||||
|             } | ||||
|     }, | ||||
|     // T | ||||
|     { | ||||
|             4, | ||||
|             7, | ||||
|             { | ||||
|                 { 3, 2, 0b0000000001110010}, | ||||
|                 { 2, 3, 0b0000000100110001}, | ||||
|                 { 3, 2, 0b0000000000100111}, | ||||
|                 { 2, 3, 0b0000001000110010} | ||||
|             } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| class Piece | ||||
| { | ||||
| private: | ||||
| public: | ||||
|     uint8_t x; | ||||
|     uint8_t y; | ||||
|     PieceData* pieceData; | ||||
|     uint8_t rotation; | ||||
|     uint8_t landingY; | ||||
|  | ||||
|     Piece(uint8_t pieceIndex = 0): | ||||
|         x(0), | ||||
|         y(0), | ||||
|         rotation(0), | ||||
|         landingY(0) | ||||
|     { | ||||
|         this->pieceData = &piecesData[pieceIndex]; | ||||
|     } | ||||
|  | ||||
|     void reset() | ||||
|     { | ||||
|         this->rotation = 0; | ||||
|         this->x = 0; | ||||
|         this->y = 0; | ||||
|         this->landingY = 0; | ||||
|     } | ||||
|  | ||||
|     uint32_t getGridRow(uint8_t x, uint8_t y, uint8_t width) | ||||
|     { | ||||
|         if (x < width) | ||||
|         { | ||||
|             //shift the row with the "top-left" position to the "x" position | ||||
|             auto shiftx = (width - 1) - x; | ||||
|             auto topleftx = (getRotation().width - 1); | ||||
|  | ||||
|             auto finalShift = shiftx - topleftx; | ||||
|             auto row = getRow(y); | ||||
|             auto finalResult = row << finalShift; | ||||
|  | ||||
|             return finalResult; | ||||
|         } | ||||
|         return 0xffffffff; | ||||
|     } | ||||
|  | ||||
|     uint8_t getRow(uint8_t y) | ||||
|     { | ||||
|         if (y < 4) | ||||
|         { | ||||
|             return (getRotation().rows >> (12 - (4 * y))) & 0xf; | ||||
|         } | ||||
|         return 0xf; | ||||
|     } | ||||
|  | ||||
|     bool getPixel(uint8_t x, uint8_t y) | ||||
|     { | ||||
|         if(x > getRotation().width - 1 || y > getRotation().height - 1 ) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|         if (x < 4 && y < 4) | ||||
|         { | ||||
|             return (getRow((4 - getRotation().height) + y) >> (3 - ((4 - getRotation().width) + x))) & 0x1; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     PieceRotation getRotation() | ||||
|     { | ||||
|         return this->pieceData->rotations[rotation]; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __PIECES_H__ */ | ||||
							
								
								
									
										64
									
								
								usermods/TetrisAI_v2/rating.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								usermods/TetrisAI_v2/rating.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : rating.h | ||||
|   * @brief          : contains the tetris rating of a grid | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2022 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __RATING_H__ | ||||
| #define __RATING_H__ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <float.h> | ||||
| #include <stdbool.h> | ||||
| #include <math.h> | ||||
| #include <vector> | ||||
| #include "rating.h" | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| class Rating | ||||
| { | ||||
| private: | ||||
| public: | ||||
|     uint8_t minHeight; | ||||
|     uint8_t maxHeight; | ||||
|     uint16_t holes; | ||||
|     uint8_t fullLines; | ||||
|     uint16_t bumpiness; | ||||
|     uint16_t aggregatedHeight; | ||||
|     double score; | ||||
|     uint8_t width; | ||||
|     std::vector<uint8_t> lineHights; | ||||
|  | ||||
|     Rating(uint8_t width): | ||||
|         width(width), | ||||
|         lineHights(width) | ||||
|     { | ||||
|         reset(); | ||||
|     } | ||||
|  | ||||
|     void reset() | ||||
|     { | ||||
|         this->minHeight = 0; | ||||
|         this->maxHeight = 0; | ||||
|  | ||||
|         for (uint8_t line = 0; line < this->width; line++) | ||||
|         { | ||||
|             this->lineHights[line] = 0; | ||||
|         } | ||||
|  | ||||
|         this->holes = 0; | ||||
|         this->fullLines = 0; | ||||
|         this->bumpiness = 0; | ||||
|         this->aggregatedHeight = 0; | ||||
|         this->score = -DBL_MAX; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __RATING_H__ */ | ||||
							
								
								
									
										33
									
								
								usermods/TetrisAI_v2/readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								usermods/TetrisAI_v2/readme.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| # Tetris AI effect usermod | ||||
|  | ||||
| This usermod brings you a effect brings a self playing Tetris game. The mod needs version 0.14 or above as it is based on matrix support. The effect was tested on an ESP32 with a WS2812B 16x16 matrix. | ||||
|  | ||||
| Version 1.0 | ||||
|  | ||||
| ## Installation  | ||||
|  | ||||
| Just activate the usermod with `-D USERMOD_TETRISAI` and the effect will become available under the name 'Tetris AI'. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| It is best to set the background color to black, the border color to light grey and the game over color (foreground) to dark grey. | ||||
|  | ||||
| ### Sliders and boxes | ||||
|  | ||||
| #### Sliders | ||||
|  | ||||
| * speed: speed the game plays | ||||
| * look ahead: how many pieces is the AI allowed to know the next pieces (0 - 2) | ||||
| * intelligence: how good the AI will play | ||||
| * Rotate color: make the colors shift (rotate) every few cicles | ||||
| * Mistakes free: how many good moves between mistakes (if activated) | ||||
|  | ||||
| #### Checkboxes | ||||
|  | ||||
| * show next: if true a space of 5 pixels from the right is used to show the next pieces. The whole segment is used for the grid otherwise. | ||||
| * show border: if true an additional column of 1 pixel is used to draw a border between the grid and the next pieces | ||||
| * mistakes: if true the worst instead of the best move is choosen every few moves (read above) | ||||
|  | ||||
| ## Best results | ||||
|  | ||||
|  If the speed is set to be a little bit faster than a good human could play with maximal intelligence and very few mistakes it makes people furious/happy at a party. | ||||
							
								
								
									
										302
									
								
								usermods/TetrisAI_v2/tetrisai.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								usermods/TetrisAI_v2/tetrisai.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : ai.h | ||||
|   * @brief          : contains the heuristic | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2023 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __AI_H__ | ||||
| #define __AI_H__ | ||||
|  | ||||
| #include "gridbw.h" | ||||
| #include "rating.h" | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| class TetrisAI | ||||
| { | ||||
| private: | ||||
| public: | ||||
|     double aHeight; | ||||
|     double fullLines; | ||||
|     double holes; | ||||
|     double bumpiness; | ||||
|     bool findWorstMove = false; | ||||
|  | ||||
|     uint8_t countOnes(uint32_t vector) | ||||
|     { | ||||
|         uint8_t count = 0; | ||||
|         while (vector) | ||||
|         { | ||||
|             vector &= (vector - 1); | ||||
|             count++; | ||||
|         } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     void updateRating(GridBW grid, Rating* rating) | ||||
|     { | ||||
|         rating->minHeight = 0; | ||||
|         rating->maxHeight = 0; | ||||
|         rating->holes = 0; | ||||
|         rating->fullLines = 0; | ||||
|         rating->bumpiness = 0; | ||||
|         rating->aggregatedHeight = 0; | ||||
|         fill(rating->lineHights.begin(), rating->lineHights.end(), 0); | ||||
|  | ||||
|         uint32_t columnvector = 0x0; | ||||
|         uint32_t lastcolumnvector = 0x0; | ||||
|         for (uint8_t row = 0; row < grid.height; row++) | ||||
|         { | ||||
|             columnvector |= grid.pixels[row]; | ||||
|  | ||||
|             //first (highest) column makes it | ||||
|             if (rating->maxHeight == 0 && columnvector) | ||||
|             { | ||||
|                 rating->maxHeight = grid.height - row; | ||||
|             } | ||||
|  | ||||
|             //if column vector is full we found the minimal height (or it stays zero) | ||||
|             if (rating->minHeight == 0 && (columnvector == (uint32_t)((1 << grid.width) - 1))) | ||||
|             { | ||||
|                 rating->minHeight = grid.height - row; | ||||
|             } | ||||
|  | ||||
|             //line full if all ones in mask :-) | ||||
|             if (grid.isLineFull(row)) | ||||
|             { | ||||
|                 rating->fullLines++; | ||||
|             } | ||||
|  | ||||
|             //holes are basically a XOR with the "full" columns | ||||
|             rating->holes += countOnes(columnvector ^ grid.pixels[row]); | ||||
|  | ||||
|             //calculate the difference (XOR) between the current column vector and the last one | ||||
|             uint32_t columnDelta = columnvector ^ lastcolumnvector; | ||||
|  | ||||
|             //process every new column | ||||
|             uint8_t index = 0; | ||||
|             while (columnDelta) | ||||
|             { | ||||
|                 //if this is a new column | ||||
|                 if (columnDelta & 0x1) | ||||
|                 { | ||||
|                     //update hight of this column | ||||
|                     rating->lineHights[(grid.width - 1) - index] = grid.height - row; | ||||
|  | ||||
|                     // update aggregatedHeight | ||||
|                     rating->aggregatedHeight += grid.height - row; | ||||
|                 } | ||||
|                 index++; | ||||
|                 columnDelta >>= 1; | ||||
|             } | ||||
|             lastcolumnvector = columnvector; | ||||
|         } | ||||
|  | ||||
|         //compare every two columns to get the difference and add them up | ||||
|         for (uint8_t column = 1; column < grid.width; column++) | ||||
|         { | ||||
|             rating->bumpiness += abs(rating->lineHights[column - 1] - rating->lineHights[column]); | ||||
|         } | ||||
|  | ||||
|         rating->score = (aHeight * (rating->aggregatedHeight)) + (fullLines * (rating->fullLines)) + (holes * (rating->holes)) + (bumpiness * (rating->bumpiness)); | ||||
|     } | ||||
|  | ||||
|     TetrisAI(): TetrisAI(-0.510066, 0.760666, -0.35663, -0.184483) | ||||
|     {} | ||||
|  | ||||
|     TetrisAI(double aHeight, double fullLines, double holes, double bumpiness): | ||||
|         aHeight(aHeight), | ||||
|         fullLines(fullLines), | ||||
|         holes(holes), | ||||
|         bumpiness(bumpiness) | ||||
|     {} | ||||
|  | ||||
|     void findBestMove(GridBW grid, Piece *piece) | ||||
|     { | ||||
|         vector<Piece> pieces = {*piece}; | ||||
|         findBestMove(grid, &pieces); | ||||
|         *piece = pieces[0]; | ||||
|     } | ||||
|  | ||||
|     void findBestMove(GridBW grid, std::vector<Piece> *pieces) | ||||
|     { | ||||
|         findBestMove(grid, pieces->begin(), pieces->end()); | ||||
|     } | ||||
|  | ||||
|     void findBestMove(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end) | ||||
|     { | ||||
|         Rating bestRating(grid.width); | ||||
|         findBestMove(grid, start, end, &bestRating); | ||||
|     } | ||||
|  | ||||
|     void findBestMove(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating) | ||||
|     { | ||||
|         grid.cleanupFullLines(); | ||||
|         Rating curRating(grid.width); | ||||
|         Rating deeperRating(grid.width); | ||||
|         Piece piece = *start; | ||||
|  | ||||
|         // for every rotation of the piece | ||||
|         for (piece.rotation = 0; piece.rotation < piece.pieceData->rotCount; piece.rotation++) | ||||
|         { | ||||
|             // put piece to top left corner | ||||
|             piece.x = 0; | ||||
|             piece.y = 0; | ||||
|  | ||||
|             //test for every column | ||||
|             for (piece.x = 0; piece.x <= grid.width - piece.getRotation().width; piece.x++) | ||||
|             { | ||||
|                 //todo optimise by the use of the previous grids height | ||||
|                 piece.landingY = 0; | ||||
|                 //will set landingY to final position | ||||
|                 grid.findLandingPosition(&piece); | ||||
|  | ||||
|                 // draw piece | ||||
|                 grid.placePiece(&piece, piece.x, piece.landingY); | ||||
|  | ||||
|                 if(start == end - 1) | ||||
|                 { | ||||
|                     //at the deepest level | ||||
|                     updateRating(grid, &curRating); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //go deeper to take another piece into account | ||||
|                     findBestMove(grid, start + 1, end, &deeperRating); | ||||
|                     curRating = deeperRating; | ||||
|                 } | ||||
|  | ||||
|                 // eraese piece | ||||
|                 grid.erasePiece(&piece, piece.x, piece.landingY); | ||||
|  | ||||
|                 if(findWorstMove) | ||||
|                 { | ||||
|                     //init rating for worst | ||||
|                     if(bestRating->score == -DBL_MAX) | ||||
|                     { | ||||
|                         bestRating->score = DBL_MAX; | ||||
|                     } | ||||
|  | ||||
|                     // update if we found a worse one | ||||
|                     if (bestRating->score > curRating.score) | ||||
|                     { | ||||
|                         *bestRating = curRating; | ||||
|                         (*start) = piece; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // update if we found a better one | ||||
|                     if (bestRating->score < curRating.score) | ||||
|                     { | ||||
|                         *bestRating = curRating; | ||||
|                         (*start) = piece; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bool findBestMoveNonBlocking(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating) | ||||
|     { | ||||
|         //vector with pieces | ||||
|         //for every piece | ||||
|             //for every  | ||||
|         switch (expression) | ||||
|         { | ||||
|             case INIT: | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bool findBestMoveNonBlocking(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating) | ||||
|     { | ||||
|         //INIT | ||||
|         grid.cleanupFullLines(); | ||||
|         Rating curRating(grid.width); | ||||
|         Rating deeperRating(grid.width); | ||||
|         Piece piece = *start; | ||||
|  | ||||
|         // for every rotation of the piece | ||||
|         piece.rotation = 0; | ||||
|  | ||||
|         //HANDLE | ||||
|         while (piece.rotation < piece.pieceData->rotCount) | ||||
|         { | ||||
|             // put piece to top left corner | ||||
|             piece.x = 0; | ||||
|             piece.y = 0; | ||||
|  | ||||
|             //test for every column | ||||
|             piece.x = 0; | ||||
|             while (piece.x <= grid.width - piece.getRotation().width) | ||||
|             { | ||||
|  | ||||
|                 //todo optimise by the use of the previous grids height | ||||
|                 piece.landingY = 0; | ||||
|                 //will set landingY to final position | ||||
|                 grid.findLandingPosition(&piece); | ||||
|  | ||||
|                 // draw piece | ||||
|                 grid.placePiece(&piece, piece.x, piece.landingY); | ||||
|  | ||||
|                 if(start == end - 1) | ||||
|                 { | ||||
|                     //at the deepest level | ||||
|                     updateRating(grid, &curRating); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //go deeper to take another piece into account | ||||
|                     findBestMove(grid, start + 1, end, &deeperRating); | ||||
|                     curRating = deeperRating; | ||||
|                 } | ||||
|  | ||||
|                 // eraese piece | ||||
|                 grid.erasePiece(&piece, piece.x, piece.landingY); | ||||
|  | ||||
|                 if(findWorstMove) | ||||
|                 { | ||||
|                     //init rating for worst | ||||
|                     if(bestRating->score == -DBL_MAX) | ||||
|                     { | ||||
|                         bestRating->score = DBL_MAX; | ||||
|                     } | ||||
|  | ||||
|                     // update if we found a worse one | ||||
|                     if (bestRating->score > curRating.score) | ||||
|                     { | ||||
|                         *bestRating = curRating; | ||||
|                         (*start) = piece; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // update if we found a better one | ||||
|                     if (bestRating->score < curRating.score) | ||||
|                     { | ||||
|                         *bestRating = curRating; | ||||
|                         (*start) = piece; | ||||
|                     } | ||||
|                 } | ||||
|                 piece.x++; | ||||
|             } | ||||
|             piece.rotation++; | ||||
|         } | ||||
|  | ||||
|         //EXIT | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __AI_H__ */ | ||||
							
								
								
									
										150
									
								
								usermods/TetrisAI_v2/tetrisaigame.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								usermods/TetrisAI_v2/tetrisaigame.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : tetrisaigame.h | ||||
|   * @brief          : main tetris functions | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2022 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __TETRISAIGAME_H__ | ||||
| #define __TETRISAIGAME_H__ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <vector> | ||||
| #include "pieces.h" | ||||
| #include "gridcolor.h" | ||||
| #include "tetrisbag.h" | ||||
| #include "tetrisai.h" | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| class TetrisAIGame | ||||
| { | ||||
| private: | ||||
|     bool animateFallOfPiece(Piece* piece, bool skip) | ||||
|     { | ||||
|         if (skip || piece->y >= piece->landingY) | ||||
|         { | ||||
|             piece->y = piece->landingY; | ||||
|             grid.gridBW.placePiece(piece, piece->x, piece->landingY); | ||||
|             grid.placePiece(piece, piece->x, piece->y); | ||||
|             return false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // eraese last drawing | ||||
|             grid.erasePiece(piece, piece->x, piece->y); | ||||
|  | ||||
|             //move piece down | ||||
|             piece->y++; | ||||
|  | ||||
|             // draw piece | ||||
|             grid.placePiece(piece, piece->x, piece->y); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     uint8_t width; | ||||
|     uint8_t height; | ||||
|     uint8_t nLookAhead; | ||||
|     TetrisBag bag; | ||||
|     GridColor grid; | ||||
|     TetrisAI ai; | ||||
|     Piece curPiece; | ||||
|     PieceData* piecesData; | ||||
|     enum States { INIT, TEST_GAME_OVER, GET_NEXT_PIECE, FIND_BEST_MOVE, ANIMATE_MOVE, ANIMATE_GAME_OVER } state = INIT; | ||||
|  | ||||
|     TetrisAIGame(uint8_t width, uint8_t height, uint8_t nLookAhead, PieceData* piecesData, uint8_t nPieces): | ||||
|         width(width), | ||||
|         height(height), | ||||
|         nLookAhead(nLookAhead), | ||||
|         bag(nPieces, 1, nLookAhead), | ||||
|         grid(width, height + 4), | ||||
|         ai(), | ||||
|         piecesData(piecesData) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     void nextPiece() | ||||
|     { | ||||
|         grid.cleanupFullLines(); | ||||
|         bag.queuePiece(); | ||||
|     } | ||||
|  | ||||
|     void findBestMove() | ||||
|     { | ||||
|         ai.findBestMove(grid.gridBW, &bag.piecesQueue); | ||||
|     } | ||||
|  | ||||
|     bool animateFall(bool skip) | ||||
|     { | ||||
|         return animateFallOfPiece(&(bag.piecesQueue[0]), skip); | ||||
|     } | ||||
|  | ||||
|     bool isGameOver() | ||||
|     { | ||||
|         //if there is something in the 4 lines of the hidden area the game is over | ||||
|         return grid.gridBW.pixels[0] || grid.gridBW.pixels[1] || grid.gridBW.pixels[2] || grid.gridBW.pixels[3]; | ||||
|     } | ||||
|  | ||||
|     void poll() | ||||
|     { | ||||
|         switch (state) | ||||
|         { | ||||
|             case INIT: | ||||
|                 reset(); | ||||
|                 state = TEST_GAME_OVER; | ||||
|                 break; | ||||
|             case TEST_GAME_OVER: | ||||
|                 if (isGameOver()) | ||||
|                 { | ||||
|                     state = ANIMATE_GAME_OVER; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     state = GET_NEXT_PIECE; | ||||
|                 } | ||||
|                 break; | ||||
|             case GET_NEXT_PIECE: | ||||
|                 nextPiece(); | ||||
|                 state = FIND_BEST_MOVE; | ||||
|                 break; | ||||
|             case FIND_BEST_MOVE: | ||||
|                 findBestMove(); | ||||
|                 state = ANIMATE_MOVE; | ||||
|                 break; | ||||
|             case ANIMATE_MOVE: | ||||
|                 if (!animateFall(false)) | ||||
|                 { | ||||
|                     state = TEST_GAME_OVER; | ||||
|                 } | ||||
|                 break; | ||||
|             case ANIMATE_GAME_OVER: | ||||
|                 static auto curPixel = grid.pixels.size(); | ||||
|                 grid.pixels[curPixel] = 254; | ||||
|  | ||||
|                 if (curPixel == 0) | ||||
|                 { | ||||
|                     state = INIT; | ||||
|                     curPixel = grid.pixels.size(); | ||||
|                 } | ||||
|                 curPixel--; | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void reset() | ||||
|     { | ||||
|         grid.clear(); | ||||
|         bag.init(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __TETRISAIGAME_H__ */ | ||||
							
								
								
									
										100
									
								
								usermods/TetrisAI_v2/tetrisbag.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								usermods/TetrisAI_v2/tetrisbag.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| /****************************************************************************** | ||||
|   * @file           : tetrisbag.h | ||||
|   * @brief          : the tetris implementation of a random piece generator | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * Copyright (c) muebau 2022 | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   ****************************************************************************** | ||||
| */ | ||||
|  | ||||
| #ifndef __TETRISBAG_H__ | ||||
| #define __TETRISBAG_H__ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <vector> | ||||
| #include <algorithm> | ||||
|  | ||||
| #include "tetrisbag.h" | ||||
|  | ||||
| class TetrisBag | ||||
| { | ||||
| private: | ||||
| public: | ||||
|     uint8_t nPieces; | ||||
|     uint8_t nBagLength; | ||||
|     uint8_t bagIdx; | ||||
|     std::vector<uint8_t> bag; | ||||
|     std::vector<Piece> piecesQueue; | ||||
|  | ||||
|     TetrisBag(uint8_t nPieces, uint8_t nBagLength, uint8_t queueLength): | ||||
|         nPieces(nPieces), | ||||
|         nBagLength(nBagLength), | ||||
|         bag(nPieces * nBagLength), | ||||
|         piecesQueue(queueLength) | ||||
|     { | ||||
|         init(); | ||||
|     } | ||||
|  | ||||
|     void init() | ||||
|     { | ||||
|         //will shuffle the bag at first use | ||||
|         bagIdx = nPieces - 1; | ||||
|  | ||||
|         for (uint8_t bagIndex = 0; bagIndex < nPieces * nBagLength; bagIndex++) | ||||
|         { | ||||
|             bag[bagIndex] = bagIndex % nPieces; | ||||
|         } | ||||
|  | ||||
|         //will init the queue | ||||
|         for (uint8_t index = 0; index < piecesQueue.size(); index++) | ||||
|         { | ||||
|             queuePiece(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void shuffleBag() | ||||
|     { | ||||
|         uint8_t temp; | ||||
|         uint8_t swapIdx; | ||||
|         for (int index = nPieces - 1; index > 0; index--) | ||||
|         { | ||||
|             //get candidate to swap | ||||
|             swapIdx = rand() % index; | ||||
|  | ||||
|             //swap it! | ||||
|             temp = bag[swapIdx]; | ||||
|             bag[swapIdx] = bag[index]; | ||||
|             bag[index] = temp; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Piece getNextPiece() | ||||
|     { | ||||
|         bagIdx++; | ||||
|         if (bagIdx >= nPieces) | ||||
|         { | ||||
|             shuffleBag(); | ||||
|             bagIdx = 0; | ||||
|         } | ||||
|         return Piece(bag[bagIdx]); | ||||
|     } | ||||
|  | ||||
|     void queuePiece() | ||||
|     { | ||||
|         //move vector to left | ||||
|         std::rotate(piecesQueue.begin(), piecesQueue.begin() + 1, piecesQueue.end()); | ||||
|         piecesQueue[piecesQueue.size() - 1] = getNextPiece(); | ||||
|     } | ||||
|  | ||||
|     void queuePiece(uint8_t idx) | ||||
|     { | ||||
|         //move vector to left | ||||
|         std::rotate(piecesQueue.begin(), piecesQueue.begin() + 1, piecesQueue.end()); | ||||
|         piecesQueue[piecesQueue.size() - 1] = Piece(idx % nPieces); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #endif /* __TETRISBAG_H__ */ | ||||
							
								
								
									
										222
									
								
								usermods/TetrisAI_v2/usermod_v2_tetrisai.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								usermods/TetrisAI_v2/usermod_v2_tetrisai.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "wled.h" | ||||
| #include "FX.h" | ||||
| #include "fcn_declare.h" | ||||
|  | ||||
| #include "tetrisaigame.h" | ||||
| // By: muebau | ||||
|  | ||||
| typedef struct TetrisAI_data | ||||
| { | ||||
|   unsigned long lastTime = 0; | ||||
|   TetrisAIGame tetris; | ||||
|   uint8_t   intelligence; | ||||
|   uint8_t   rotate; | ||||
|   bool      showNext; | ||||
|   bool      showBorder; | ||||
|   uint8_t   colorOffset; | ||||
|   uint8_t   colorInc; | ||||
|   uint8_t   mistaceCountdown; | ||||
| } tetrisai_data; | ||||
|  | ||||
| void drawGrid(TetrisAIGame* tetris, TetrisAI_data* tetrisai_data) | ||||
| { | ||||
|   SEGMENT.fill(SEGCOLOR(1)); | ||||
|  | ||||
|   //GRID | ||||
|   for (auto index_y = 4; index_y < tetris->grid.height; index_y++) | ||||
|   { | ||||
|     for (auto index_x = 0; index_x < tetris->grid.width; index_x++) | ||||
|     { | ||||
|       CRGB color; | ||||
|       if (*tetris->grid.getPixel(index_x, index_y) == 0) | ||||
|       { | ||||
|         //BG color | ||||
|         color = SEGCOLOR(1); | ||||
|       } | ||||
|       //game over animation | ||||
|       else if(*tetris->grid.getPixel(index_x, index_y) == 254) | ||||
|       { | ||||
|         //use fg | ||||
|         color = SEGCOLOR(0); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         //spread the color over the whole palette | ||||
|         uint8_t colorIndex = *tetris->grid.getPixel(index_x, index_y) * 32; | ||||
|         colorIndex += tetrisai_data->colorOffset; | ||||
|         color = ColorFromPalette(SEGPALETTE, colorIndex, 255, NOBLEND); | ||||
|       } | ||||
|  | ||||
|       SEGMENT.setPixelColorXY(index_x, index_y - 4, color); | ||||
|     } | ||||
|   } | ||||
|   tetrisai_data->colorOffset += tetrisai_data->colorInc; | ||||
|  | ||||
|   //NEXT PIECE AREA | ||||
|   if (tetrisai_data->showNext) | ||||
|   { | ||||
|     //BORDER | ||||
|     if (tetrisai_data->showBorder) | ||||
|     { | ||||
|       //draw a line 6 pixels from right with the border color | ||||
|       for (auto index_y = 0; index_y < SEGMENT.virtualHeight(); index_y++) | ||||
|       { | ||||
|         SEGMENT.setPixelColorXY(SEGMENT.virtualWidth() - 6, index_y, SEGCOLOR(2)); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     //NEXT PIECE | ||||
|     int piecesOffsetX = SEGMENT.virtualWidth() - 4; | ||||
|     int piecesOffsetY = 1; | ||||
|     for (uint8_t nextPieceIdx = 1; nextPieceIdx < tetris->nLookAhead; nextPieceIdx++) | ||||
|     { | ||||
|       uint8_t pieceNbrOffsetY = (nextPieceIdx - 1) * 5; | ||||
|  | ||||
|       Piece piece(tetris->bag.piecesQueue[nextPieceIdx]); | ||||
|  | ||||
|       for (uint8_t pieceY = 0; pieceY < piece.getRotation().height; pieceY++) | ||||
|       { | ||||
|         for (uint8_t pieceX = 0; pieceX < piece.getRotation().width; pieceX++) | ||||
|         { | ||||
|           if (piece.getPixel(pieceX, pieceY)) | ||||
|           { | ||||
|             uint8_t colIdx = ((piece.pieceData->colorIndex * 32) + tetrisai_data->colorOffset); | ||||
|             SEGMENT.setPixelColorXY(piecesOffsetX + pieceX, piecesOffsetY + pieceNbrOffsetY + pieceY, ColorFromPalette(SEGPALETTE, colIdx, 255, NOBLEND)); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| //////////////////////////// | ||||
| //     2D Tetris AI       // | ||||
| //////////////////////////// | ||||
| uint16_t mode_2DTetrisAI() | ||||
| { | ||||
|   if (!strip.isMatrix || !SEGENV.allocateData(sizeof(tetrisai_data))) | ||||
|   { | ||||
|     // not a 2D set-up | ||||
|     SEGMENT.fill(SEGCOLOR(0)); | ||||
|     return 350; | ||||
|   } | ||||
|   TetrisAI_data* tetrisai_data = reinterpret_cast<TetrisAI_data*>(SEGENV.data); | ||||
|  | ||||
|   const uint16_t cols = SEGMENT.virtualWidth(); | ||||
|   const uint16_t rows = SEGMENT.virtualHeight(); | ||||
|  | ||||
|   //range 0 - 1024ms => 1024/255 ~ 4 | ||||
|   uint16_t msDelayMove = 1024 - (4 * SEGMENT.speed); | ||||
|   int16_t msDelayGameOver = msDelayMove / 4; | ||||
|  | ||||
|   //range 0 - 2 (not including current) | ||||
|   uint8_t nLookAhead = SEGMENT.intensity ? (SEGMENT.intensity >> 7) + 2 : 1; | ||||
|   //range 0 - 16 | ||||
|   tetrisai_data->colorInc = SEGMENT.custom2 >> 4; | ||||
|  | ||||
|   if (!tetrisai_data->tetris || (tetrisai_data->tetris.nLookAhead != nLookAhead | ||||
|     || tetrisai_data->showNext != SEGMENT.check1 | ||||
|     || tetrisai_data->showBorder != SEGMENT.check2 | ||||
|       ) | ||||
|     ) | ||||
|   { | ||||
|     tetrisai_data->showNext = SEGMENT.check1; | ||||
|     tetrisai_data->showBorder = SEGMENT.check2; | ||||
|  | ||||
|     //not more than 32 as this is the limit of this implementation | ||||
|     uint8_t gridWidth = cols < 32 ? cols : 32; | ||||
|     uint8_t gridHeight = rows; | ||||
|  | ||||
|     // do we need space for the 'next' section? | ||||
|     if (tetrisai_data->showNext) | ||||
|     { | ||||
|       // make space for the piece and one pixel of space | ||||
|       gridWidth = gridWidth - 5; | ||||
|  | ||||
|       // do we need space for a border? | ||||
|       if (tetrisai_data->showBorder) | ||||
|       { | ||||
|         gridWidth = gridWidth - 1; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     tetrisai_data->tetris = TetrisAIGame(gridWidth, gridHeight, nLookAhead, piecesData, numPieces); | ||||
|     SEGMENT.fill(SEGCOLOR(1)); | ||||
|   } | ||||
|  | ||||
|   if (tetrisai_data->intelligence != SEGMENT.custom1) | ||||
|   { | ||||
|     tetrisai_data->intelligence = SEGMENT.custom1; | ||||
|     double dui = 0.2 - (0.2 * (tetrisai_data->intelligence / 255.0)); | ||||
|  | ||||
|     tetrisai_data->tetris.ai.aHeight = -0.510066 + dui; | ||||
|     tetrisai_data->tetris.ai.fullLines = 0.760666 - dui; | ||||
|     tetrisai_data->tetris.ai.holes = -0.35663 + dui; | ||||
|     tetrisai_data->tetris.ai.bumpiness = -0.184483 + dui; | ||||
|   } | ||||
|  | ||||
|   if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_MOVE) | ||||
|   { | ||||
|     if (millis() - tetrisai_data->lastTime > msDelayMove) | ||||
|     { | ||||
|       drawGrid(&tetrisai_data->tetris, tetrisai_data); | ||||
|       tetrisai_data->lastTime = millis(); | ||||
|       tetrisai_data->tetris.poll(); | ||||
|     } | ||||
|   } | ||||
|   else if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_GAME_OVER) | ||||
|   { | ||||
|     if (millis() - tetrisai_data->lastTime > msDelayGameOver) | ||||
|     { | ||||
|       drawGrid(&tetrisai_data->tetris, tetrisai_data); | ||||
|       tetrisai_data->lastTime = millis(); | ||||
|       tetrisai_data->tetris.poll(); | ||||
|     } | ||||
|   } | ||||
|   else if (tetrisai_data->tetris.state == TetrisAIGame::FIND_BEST_MOVE) | ||||
|   { | ||||
|     if (SEGMENT.check3) | ||||
|     { | ||||
|       if(tetrisai_data->mistaceCountdown == 0) | ||||
|       { | ||||
|         tetrisai_data->tetris.ai.findWorstMove = true; | ||||
|         tetrisai_data->tetris.poll(); | ||||
|         tetrisai_data->tetris.ai.findWorstMove = false; | ||||
|         tetrisai_data->mistaceCountdown = SEGMENT.custom3; | ||||
|       } | ||||
|       tetrisai_data->mistaceCountdown--;       | ||||
|     } | ||||
|     tetrisai_data->tetris.poll(); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     tetrisai_data->tetris.poll(); | ||||
|   } | ||||
|  | ||||
|   return FRAMETIME; | ||||
| } // mode_2DTetrisAI() | ||||
| static const char _data_FX_MODE_2DTETRISAI[] PROGMEM = "Tetris AI@!,Look ahead,Intelligence,Rotate color,Mistake free,Show next,Border,Mistakes;Game Over,!,Border;!;2;sx=127,ix=64,c1=255,c2=0,c3=31,o1=1,o2=1,o3=0,pal=11"; | ||||
|  | ||||
| class TetrisAIUsermod : public Usermod | ||||
| { | ||||
|  | ||||
| private: | ||||
|  | ||||
| public: | ||||
|   void setup() | ||||
|   { | ||||
|     strip.addEffect(255, &mode_2DTetrisAI, _data_FX_MODE_2DTETRISAI); | ||||
|   } | ||||
|  | ||||
|   void loop() | ||||
|   { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   uint16_t getId() | ||||
|   { | ||||
|     return USERMOD_ID_TETRISAI; | ||||
|   } | ||||
| }; | ||||
| @@ -180,6 +180,7 @@ | ||||
| #define USERMOD_ID_STAIRWAY_WIPE         44     //Usermod "stairway-wipe-usermod-v2.h" | ||||
| #define USERMOD_ID_ANIMARTRIX            45     //Usermod "usermod_v2_animartrix.h" | ||||
| #define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46   //usermod "usermod_v2_HttpPullLightControl.h" | ||||
| #define USERMOD_ID_TETRISAI              47     //Usermod "usermod_v2_tetris.h" | ||||
|  | ||||
| //Access point behavior | ||||
| #define AP_BEHAVIOR_BOOT_NO_CONN          0     //Open AP when no connection after boot | ||||
|   | ||||
| @@ -209,6 +209,10 @@ | ||||
|   #include "../usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef USERMOD_TETRISAI | ||||
| #include "../usermods/TetrisAI_v2/usermod_v2_tetrisai.h" | ||||
| #endif | ||||
|  | ||||
| void registerUsermods() | ||||
| { | ||||
| /* | ||||
| @@ -405,4 +409,8 @@ void registerUsermods() | ||||
|   #ifdef USERMOD_STAIRCASE_WIPE | ||||
|   usermods.add(new StairwayWipeUsermod()); | ||||
|   #endif | ||||
|  | ||||
|   #ifdef USERMOD_TETRISAI | ||||
|   usermods.add(new TetrisAIUsermod()); | ||||
|   #endif | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Blaž Kristan
					Blaž Kristan