-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrandom.ts
100 lines (91 loc) · 2.56 KB
/
random.ts
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
import _ from 'lodash'
import {
BotCreateOptions,
BotImplementation,
BOT_THINKING_DELAY,
Client,
} from 'src/core/bots/framework'
import {
assertDefined,
BOARD_PUSH_POSITIONS,
findConnected,
getPieceAt,
} from 'src/core/server/board'
import * as t from 'src/gameTypes'
import { loopUntilSuccess } from 'src/utils/utils'
export const name = 'Random bot'
/**
* Warning! You must use `client` as is, because it's a Proxy object for
* reconnect purposes. Don't take any properties out of it, it will break.
*
* Do not:
*
* // Bad
* const { serverRpc } = client
*
* // Bad
* const getMyPosition = client.serverRpc.getMyPosition()
*
* Whatever you need to access within client, always use the full property path:
*
* client.serverRpc.getMyPosition()
*
* @returns Promise so that the bot creation can do something async if needed.
*/
export async function create({
logger,
client,
}: BotCreateOptions): Promise<BotImplementation> {
return {
async onMyTurn(getState) {
await new Promise((resolve) =>
setTimeout(resolve, BOT_THINKING_DELAY / 2)
)
const state = getState()
// Loop until success, because the bot doesn't understand the blocked push
// position rule
await loopUntilSuccess(
async () => {
await push(state, client)
},
{ maxTries: 5, onError: (err) => logger.error('Unable to push', err) }
)
const stateAfterPush = getState()
// Do a move
await new Promise((resolve) =>
setTimeout(resolve, BOT_THINKING_DELAY / 2)
)
await move(stateAfterPush, client)
},
}
}
async function push(_gameState: t.ClientGameState, client: Client) {
// Turn the extra piece randomly
await client.serverRpc.setExtraPieceRotation(
assertDefined(_.sample<t.Rotation>([0, 90, 180, 270]))
)
const pushPos = assertDefined(_.sample(BOARD_PUSH_POSITIONS))
await client.serverRpc.push(pushPos)
}
async function move(gameState: t.ClientGameState, client: Client) {
if (!gameState.myPosition) {
throw new Error('My position not found from state')
}
const currentPos = gameState.myPosition
const piece = getPieceAt(
gameState.board as unknown as t.FilledBoard,
currentPos
)
const connected = _.shuffle(
findConnected(
gameState.board as unknown as t.Board,
new Set([assertDefined(piece)])
)
)
// Prefer another piece
const newPos =
connected.find(
(p) => p.position.x !== currentPos.x && p.position.y !== currentPos.y
)?.position ?? currentPos
await client.serverRpc.move(newPos)
}