-
Notifications
You must be signed in to change notification settings - Fork 0
/
simulator.js
163 lines (127 loc) · 4.56 KB
/
simulator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import _ from 'lodash'
import constants from 'games/tetris/ai/constants'
import gameLogic from 'games/tetris/ai/gameLogic'
import TreeNode from 'games/tetris/ai/treeDataStructure'
function makeMove (currentBlock, move, checkCollisionFn) {
if (move === 'right' || move === 'left') {
currentBlock.move(move)
} else if (move === 'down') {
while (currentBlock.isMovable) {
// The advance function will move the block down
currentBlock.advance(checkCollisionFn)
}
} else if (move === 'rotate') {
currentBlock.changeRotation()
}
return currentBlock
}
function generateMoves (currentBlock, checkCollisionFn) {
if (!currentBlock.isMovable) {
return []
}
const possibleMoves = _.map(['left', 'right', 'rotate'], function (move) {
return makeMove(_.cloneDeep(currentBlock), move)
})
possibleMoves.push(makeMove(_.cloneDeep(currentBlock), 'down', checkCollisionFn))
return possibleMoves
}
function stripDuplicateMoves (newBlockMoves, allBlockMoveNodes) {
const uniqueBlockMoves = []
_.each(newBlockMoves, function (newBlockMove) {
const duplicateBlock = _.find(allBlockMoveNodes, function (blockMoveNode) {
return _.isEqual(blockMoveNode.block.occupiedPositions, newBlockMove.occupiedPositions)
})
if (!duplicateBlock) {
uniqueBlockMoves.push(newBlockMove)
}
})
return uniqueBlockMoves
}
function isFivePercentChance () {
// generate random number
// the chance of that number being 7 in this case is 5%
// return false
const isIt = _.random(19) === 7
if (isIt) {
console.log('5% PERCENT CHANCE MOVE!')
}
return isIt
}
function generateAllMoveNodes (tetrisGame) {
let allMoveNodes = [new TreeNode(null, tetrisGame.getCurrentBlock())]
let blockPositions = [_.cloneDeep(tetrisGame.getCurrentBlock())]
while (_.size(blockPositions)) {
const parentMove = blockPositions.pop()
const newMoves = generateMoves(parentMove, tetrisGame.getCheckCollisionFn())
const newUniqueMoves = stripDuplicateMoves(newMoves, allMoveNodes)
const uniqueMoveNodes = _.map(newUniqueMoves, function (uniqueMove) {
const newChild = new TreeNode(null, uniqueMove)
return newChild
})
allMoveNodes = _.concat(uniqueMoveNodes, allMoveNodes)
blockPositions = _.concat(newUniqueMoves, blockPositions)
}
return allMoveNodes
}
function getFinalMoves (moveNodes) {
return _(moveNodes).map(function (moveNode) {
if (!moveNode.block.isMovable) {
return moveNode
}
})
.compact()
.value()
}
function getBestMoveNode (tetrisGame, netConfig) {
const finalMoves = getFinalMoves(generateAllMoveNodes(tetrisGame))
let bestMoves = {moveValue: -100000, sameValueMoveIndexes: []}
// add random function
// WATCH OUT FOR BOARD VECTOR GENERATION!
_.each(finalMoves, function (moveNode, index) {
const board = tetrisGame.getBoard()
const occupiedRows = gameLogic.populateLowestFourYCoordsFromOccupiedPositions(board)
// BOARD CHANGED BY REFERENCE
gameLogic.populateBoardWithActualMove(board, moveNode.block.occupiedPositions, constants.generic.FILLED_CELL_VALUE)
const fullRowCount = gameLogic.getFullRowCount(board, occupiedRows)
// reward is just calculating full rows or game lost
const reward = gameLogic.getMoveValue(fullRowCount, _.min(occupiedRows))
moveNode.setReward(reward)
moveNode.setBoardVector(board, occupiedRows)
// moveNode.board = _.cloneDeep(board)
gameLogic.populateBoardWithActualMove(board, moveNode.block.occupiedPositions)
const moveValue = reward + netConfig.net.run(moveNode.boardVector)[0]
// let moveValue = netConfig.net.run(moveNode.boardVector)[0]
if (moveValue === bestMoves.moveValue) {
bestMoves.sameValueMoveIndexes.push(index)
}
if (moveValue > bestMoves.moveValue) {
bestMoves = {
moveValue,
sameValueMoveIndexes: [index],
}
}
})
const bestMoveIndex = bestMoves.sameValueMoveIndexes[_.random(_.size(bestMoves.sameValueMoveIndexes) - 1)]
if (isFivePercentChance()) {
const randomIndex = _.random(_.size(finalMoves) - 1)
return finalMoves[randomIndex]
}
return finalMoves[bestMoveIndex]
}
function playOneEpisode (tetrisGame, netConfig) {
const allBestMoveNodes = []
let gameMoves = 0
while (!tetrisGame.isGameOver()) {
const bestMoveNode = getBestMoveNode(tetrisGame, netConfig)
if (!bestMoveNode || gameMoves > constants.ai.MAX_GAME_MOVES) {
break
}
allBestMoveNodes.push(bestMoveNode)
tetrisGame.AIAdvanceGame(bestMoveNode.block)
gameMoves++
}
return allBestMoveNodes
}
export default {
playOneEpisode,
}