* Perform a step of the game. * * @param {0 | 1 | 2 | 3} action The action to take in the current step. * The meaning of the possible values: * 0 - left * 1 - top * 2 - right * 3 - bottom * @return {object} Object with the following keys: * - `reward` {
(action)
| 120 | * over its own body. |
| 121 | */ |
| 122 | step(action) { |
| 123 | const [headY, headX] = this.snakeSquares_[0]; |
| 124 | |
| 125 | // Calculate the coordinates of the new head and check whether it has |
| 126 | // gone off the board, in which case the game will end. |
| 127 | let done; |
| 128 | let newHeadY; |
| 129 | let newHeadX; |
| 130 | |
| 131 | this.updateDirection_(action); |
| 132 | if (this.snakeDirection_ === 'l') { |
| 133 | newHeadY = headY; |
| 134 | newHeadX = headX - 1; |
| 135 | done = newHeadX < 0; |
| 136 | } else if (this.snakeDirection_ === 'u') { |
| 137 | newHeadY = headY - 1; |
| 138 | newHeadX = headX; |
| 139 | done = newHeadY < 0 |
| 140 | } else if (this.snakeDirection_ === 'r') { |
| 141 | newHeadY = headY; |
| 142 | newHeadX = headX + 1; |
| 143 | done = newHeadX >= this.width_; |
| 144 | } else if (this.snakeDirection_ === 'd') { |
| 145 | newHeadY = headY + 1; |
| 146 | newHeadX = headX; |
| 147 | done = newHeadY >= this.height_; |
| 148 | } |
| 149 | |
| 150 | // Check if the head goes over the snake's body, in which case the |
| 151 | // game will end. |
| 152 | for (let i = 1; i < this.snakeSquares_.length; ++i) { |
| 153 | if (this.snakeSquares_[i][0] === newHeadY && |
| 154 | this.snakeSquares_[i][1] === newHeadX) { |
| 155 | done = true; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | let fruitEaten = false; |
| 160 | if (done) { |
| 161 | return {reward: DEATH_REWARD, done, fruitEaten}; |
| 162 | } |
| 163 | |
| 164 | // Update the position of the snake. |
| 165 | this.snakeSquares_.unshift([newHeadY, newHeadX]); |
| 166 | |
| 167 | // Check if a fruit is eaten. |
| 168 | let reward = NO_FRUIT_REWARD; |
| 169 | for (let i = 0; i < this.fruitSquares_.length; ++i) { |
| 170 | const fruitYX = this.fruitSquares_[i]; |
| 171 | if (fruitYX[0] === newHeadY && fruitYX[1] === newHeadX) { |
| 172 | reward = FRUIT_REWARD; |
| 173 | fruitEaten = true; |
| 174 | this.fruitSquares_.splice(i, 1); |
| 175 | this.makeFruits_(); |
| 176 | break; |
| 177 | } |
| 178 | } |
| 179 | if (!fruitEaten) { |
no test coverage detected