-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.ts
201 lines (177 loc) · 6.9 KB
/
index.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
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
type StateType = [number, number, TURN]
type ResultType = [
mouseResult: [result: RESULT_FLAG, move: number],
catResult: [result: RESULT_FLAG, move: number],
]
enum TURN {
MOUSE_TURN = 0,
CAT_TURN = 1,
}
enum RESULT_FLAG {
UNKNOWN = 0,
MOUSE_WIN = 1,
CAT_WIN = 2,
}
const MAX_MOVES = 1000
const DIRS = [[-1, 0], [1, 0], [0, -1], [0, 1]]
/**
* 拓扑排序
* @desc 时间复杂度 O(M²N²(M+N)) 空间复杂度 O(M²N²)
* @param grid
* @param catJump
* @param mouseJump
* @returns
*/
export function canMouseWin(
grid: string[],
catJump: number,
mouseJump: number,
): boolean {
const rows = grid.length
const cols = grid[0].length
let startMouse = -1
let startCat = -1
let food = -1
const getPosKey = (row: number, col: number): number => row * cols + col
const getPos = (key: number): [number, number] => [(key / cols) >> 0, key % cols]
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const c = grid[i][j]
if (c === 'M')
startMouse = getPosKey(i, j)
else if (c === 'C')
startCat = getPosKey(i, j)
else if (c === 'F')
food = getPosKey(i, j)
}
}
const total = rows * cols
const degrees: [mouse: number, cat: number][][]
= new Array(total).fill(0).map(() => new Array(total).fill(0).map(() => [0, 0]))
// 计算每个状态的度
for (let mouse = 0; mouse < total; mouse++) {
const [mouseRow, mouseCol] = getPos(mouse)
if (grid[mouseRow][mouseCol] === '#') continue
for (let cat = 0; cat < total; cat++) {
const [catRow, catCol] = getPos(cat)
if (grid[catRow][catCol] === '#') continue
degrees[mouse][cat][TURN.MOUSE_TURN]++
degrees[mouse][cat][TURN.CAT_TURN]++
for (const dir of DIRS) {
for (
let row = mouseRow + dir[0], col = mouseCol + dir[1], jump = 1;
row >= 0 && row < rows && col >= 0 && col < cols && grid[row][col] !== '#' && jump <= mouseJump;
row += dir[0], col += dir[1], jump++
) {
const nextMouse = getPosKey(row, col)
const nextCat = getPosKey(catRow, catCol)
degrees[nextMouse][nextCat][TURN.MOUSE_TURN]++
}
for (
let row = catRow + dir[0], col = catCol + dir[1], jump = 1;
row >= 0 && row < rows && col >= 0 && col < cols && grid[row][col] !== '#' && jump <= catJump;
row += dir[0], col += dir[1], jump++
) {
const nextMouse = getPosKey(mouseRow, mouseCol)
const nextCat = getPosKey(row, col)
degrees[nextMouse][nextCat][TURN.CAT_TURN]++
}
}
}
}
const results: ResultType[][]
= new Array(total).fill(0)
.map(() => new Array(total).fill(0)
.map(() => [[RESULT_FLAG.UNKNOWN, 0], [RESULT_FLAG.UNKNOWN, 0]]),
)
const queue: StateType[] = []
// 猫和老鼠在同一个单元格,猫获胜
for (let pos = 0; pos < total; pos++) {
const [row, col] = getPos(pos)
if (grid[row][col] === '#') continue
results[pos][pos][TURN.MOUSE_TURN][0] = RESULT_FLAG.CAT_WIN
results[pos][pos][TURN.MOUSE_TURN][1] = 0
results[pos][pos][TURN.CAT_TURN][0] = RESULT_FLAG.CAT_WIN
results[pos][pos][TURN.CAT_TURN][1] = 0
queue.push([pos, pos, TURN.MOUSE_TURN])
queue.push([pos, pos, TURN.CAT_TURN])
}
// 猫和食物在同一个单元格,猫获胜
for (let mouse = 0; mouse < total; mouse++) {
const [mouseRow, mouseCol] = getPos(mouse)
if (grid[mouseRow][mouseCol] === '#' || mouse === food) continue
results[mouse][food][TURN.MOUSE_TURN][0] = RESULT_FLAG.CAT_WIN
results[mouse][food][TURN.MOUSE_TURN][1] = 0
results[mouse][food][TURN.CAT_TURN][0] = RESULT_FLAG.CAT_WIN
results[mouse][food][TURN.CAT_TURN][1] = 0
queue.push([mouse, food, TURN.MOUSE_TURN])
queue.push([mouse, food, TURN.CAT_TURN])
}
// 老鼠和食物在同一个单元格且猫和食物不在同一个单元格,老鼠获胜
for (let cat = 0; cat < total; cat++) {
const [catRow, catCol] = getPos(cat)
if (grid[catRow][catCol] === '#' || cat === food) continue
results[food][cat][TURN.MOUSE_TURN][0] = RESULT_FLAG.MOUSE_WIN
results[food][cat][TURN.MOUSE_TURN][1] = 0
results[food][cat][TURN.CAT_TURN][0] = RESULT_FLAG.MOUSE_WIN
results[food][cat][TURN.CAT_TURN][1] = 0
queue.push([food, cat, TURN.MOUSE_TURN])
queue.push([food, cat, TURN.CAT_TURN])
}
// 拓扑排序
while (queue.length) {
const [mouse, cat, turn] = queue.shift()!
const [result, moves] = results[mouse][cat][turn]
const prevStates = getPrevStates(mouse, cat, turn)
for (const prevState of prevStates) {
const [prevMouse, prevCat, prevTurn] = prevState
if (results[prevMouse][prevCat][prevTurn][0] === RESULT_FLAG.UNKNOWN) {
const canWin
= (result === RESULT_FLAG.MOUSE_WIN && prevTurn === TURN.MOUSE_TURN)
|| (result === RESULT_FLAG.CAT_WIN && prevTurn === TURN.CAT_TURN)
if (canWin) {
results[prevMouse][prevCat][prevTurn][0] = result
results[prevMouse][prevCat][prevTurn][1] = moves + 1
queue.push([prevMouse, prevCat, prevTurn])
}
else {
degrees[prevMouse][prevCat][prevTurn]--
if (degrees[prevMouse][prevCat][prevTurn] === 0) {
const loseResult = prevTurn === TURN.MOUSE_TURN ? RESULT_FLAG.CAT_WIN : RESULT_FLAG.MOUSE_WIN
results[prevMouse][prevCat][prevTurn][0] = loseResult
results[prevMouse][prevCat][prevTurn][1] = moves + 1
queue.push([prevMouse, prevCat, prevTurn])
}
}
}
}
}
const [result, move] = results[startMouse][startCat][TURN.MOUSE_TURN]
return result === RESULT_FLAG.MOUSE_WIN && move <= MAX_MOVES
function getPrevStates(mouse: number, cat: number, turn: TURN): StateType[] {
const prevStates: StateType[] = []
const [mouseRow, mouseCol] = getPos(mouse)
const [catRow, catCol] = getPos(cat)
const prevTurn = turn === TURN.MOUSE_TURN ? TURN.CAT_TURN : TURN.MOUSE_TURN
const maxJump = prevTurn === TURN.MOUSE_TURN ? mouseJump : catJump
const startRow = prevTurn === TURN.MOUSE_TURN ? mouseRow : catRow
const startCol = prevTurn === TURN.MOUSE_TURN ? mouseCol : catCol
prevStates.push([mouse, cat, prevTurn])
for (const dir of DIRS) {
for (
let i = startRow + dir[0], j = startCol + dir[1], jump = 1;
i >= 0 && i < rows && j >= 0 && j < cols && grid[i][j] !== '#' && jump <= maxJump;
i += dir[0], j += dir[1], jump++
) {
const prevMouseRow = prevTurn === TURN.MOUSE_TURN ? i : mouseRow
const prevMouseCol = prevTurn === TURN.MOUSE_TURN ? j : mouseCol
const prevCatRow = prevTurn === TURN.MOUSE_TURN ? catRow : i
const prevCatCol = prevTurn === TURN.MOUSE_TURN ? catCol : j
const prevMouse = getPosKey(prevMouseRow, prevMouseCol)
const prevCat = getPosKey(prevCatRow, prevCatCol)
prevStates.push([prevMouse, prevCat, prevTurn])
}
}
return prevStates
}
}