fork download
  1. import math
  2. import random
  3. import time
  4.  
  5.  
  6. def play(gameState, memo={}):
  7. start_time = time.process_time()
  8. turn = gameState.currentTurn
  9. maxT = gameState.maxTurn
  10.  
  11. INF = 10 ** 9
  12. WIN = 100000
  13. PLYLIM = 44
  14.  
  15. W = gameState.width
  16. H = gameState.height
  17. goal_mode = gameState.goalMode
  18.  
  19. walls_xy = frozenset((w.x, w.y) for w in gameState.walls)
  20. goals0_xy = frozenset((g.x, g.y) for g in gameState.goals) or frozenset([(W, H)])
  21. goals1_xy = frozenset((g.x, g.y) for g in gameState.enemyGoals) or frozenset([(1, 1)])
  22.  
  23. # Bảng khắc chế: 0=R (Búa), 1=P (Bao), 2=S (Kéo)
  24. BT = ((False, False, True), (True, False, False), (False, True, False))
  25. PRED = (1, 2, 0)
  26. PREY = (2, 0, 1)
  27.  
  28. ELIM1 = 65
  29. ELIM2 = 0
  30.  
  31. # Nếu trận trước bị reset giữa chừng thì xóa cache cũ
  32. if turn <= 1 and 'tabs' in memo:
  33. memo.clear()
  34.  
  35. map_sig = (W, H, walls_xy, goals0_xy, goals1_xy, goal_mode)
  36. tabs = memo.get('tabs')
  37.  
  38. # Khởi tạo bảng tra cứu bản đồ (chạy 1 lần ở lượt đầu hoặc khi đổi map)
  39. if tabs is None or memo.get('map_sig') != map_sig:
  40. memo['map_sig'] = map_sig
  41. memo['tt'] = {}
  42. memo['hist'] = None
  43. memo['seen'] = {}
  44. memo['cfg'] = {}
  45.  
  46. STRIDE = W + 2
  47. SIZE = (W + 2) * (H + 2)
  48. OFFS = (STRIDE + 1, STRIDE, 1, STRIDE - 1, -(STRIDE - 1), -1, -STRIDE, -(STRIDE + 1))
  49. DIRN = {
  50. STRIDE - 1: 1,
  51. STRIDE: 2,
  52. STRIDE + 1: 3,
  53. 1: 4,
  54. -(STRIDE - 1): 5,
  55. -STRIDE: 6,
  56. -(STRIDE + 1): 7,
  57. -1: 8,
  58. }
  59.  
  60. wall_set = frozenset(
  61. y * STRIDE + x for (x, y) in walls_xy if 1 <= x <= W and 1 <= y <= H
  62. )
  63. cells = [
  64. y * STRIDE + x
  65. for y in range(1, H + 1)
  66. for x in range(1, W + 1)
  67. if (y * STRIDE + x) not in wall_set
  68. ]
  69. valid_set = set(cells)
  70.  
  71. GOAL0 = frozenset(y * STRIDE + x for (x, y) in goals0_xy if (y * STRIDE + x) in valid_set)
  72. GOAL1 = frozenset(y * STRIDE + x for (x, y) in goals1_xy if (y * STRIDE + x) in valid_set)
  73. GOAL = (GOAL0, GOAL1)
  74.  
  75. nb0_set = {g + off for g in GOAL0 for off in OFFS if (g + off) in valid_set and (g + off) not in GOAL0}
  76. nb1_set = {g + off for g in GOAL1 for off in OFFS if (g + off) in valid_set and (g + off) not in GOAL1}
  77. NB = (tuple(nb0_set), tuple(nb1_set))
  78. DEF_SQ = (GOAL0 | nb0_set, GOAL1 | nb1_set)
  79.  
  80. MAX_D = W + H + 12
  81.  
  82. # BFS tính khoảng cách ngắn nhất né tường tới tập ô đích của ta (D0) và địch (D1)
  83. D0 = [MAX_D] * SIZE
  84. bfs_q = list(GOAL0)
  85. for g in GOAL0:
  86. D0[g] = 0
  87. head = 0
  88. while head < len(bfs_q):
  89. u = bfs_q[head]
  90. head += 1
  91. du = D0[u] + 1
  92. for off in OFFS:
  93. v = u + off
  94. if v in valid_set and du < D0[v]:
  95. D0[v] = du
  96. bfs_q.append(v)
  97.  
  98. D1 = [MAX_D] * SIZE
  99. bfs_q = list(GOAL1)
  100. for g in GOAL1:
  101. D1[g] = 0
  102. head = 0
  103. while head < len(bfs_q):
  104. u = bfs_q[head]
  105. head += 1
  106. du = D1[u] + 1
  107. for off in OFFS:
  108. v = u + off
  109. if v in valid_set and du < D1[v]:
  110. D1[v] = du
  111. bfs_q.append(v)
  112.  
  113. # BFS tính khoảng cách thực tế giữa mọi cặp ô hợp lệ để săn đuổi vòng qua vật cản
  114. DT = [[MAX_D] * SIZE for _ in range(SIZE)]
  115. MDT = [[MAX_D * 2] * SIZE for _ in range(SIZE)]
  116. for src in cells:
  117. row = DT[src]
  118. mrow = MDT[src]
  119. row[src] = 0
  120. mrow[src] = 0
  121. bfs_q = [src]
  122. head = 0
  123. while head < len(bfs_q):
  124. u = bfs_q[head]
  125. head += 1
  126. du = row[u] + 1
  127. for off in OFFS:
  128. v = u + off
  129. if v in valid_set and du < row[v]:
  130. row[v] = du
  131. mrow[v] = du * 2
  132. bfs_q.append(v)
  133.  
  134. max_d0 = max((D0[a] for a in cells if D0[a] < MAX_D), default=1)
  135. max_d1 = max((D1[a] for a in cells if D1[a] < MAX_D), default=1)
  136. ADV0 = [0] * SIZE
  137. ADV1 = [0] * SIZE
  138. CENTER = [0] * SIZE
  139. mid_x = (W + 1) * 0.5
  140. mid_y = (H + 1) * 0.5
  141. for a in cells:
  142. x = a % STRIDE
  143. y = a // STRIDE
  144. ADV0[a] = max(0, max_d0 - D0[a])
  145. ADV1[a] = max(0, max_d1 - D1[a])
  146. cx = abs(x - mid_x) / max(1.0, W / 9.0)
  147. cy = abs(y - mid_y) / max(1.0, H / 9.0)
  148. CENTER[a] = max(0, int(4 - max(cx, cy)))
  149.  
  150. # Bảng nước đi hợp lệ sắp xếp sẵn theo độ giảm khoảng cách tới đích
  151. MTAB0 = [() for _ in range(SIZE)]
  152. MTAB1 = [() for _ in range(SIZE)]
  153. for a in cells:
  154. m0 = []
  155. m1 = []
  156. for off in OFFS:
  157. q = a + off
  158. if q in valid_set:
  159. h_ix = a * SIZE + q
  160. fw0_b = (D0[a] - D0[q]) * 2
  161. fw1_b = (D1[a] - D1[q]) * 2
  162. m0.append((q, fw0_b, h_ix, 1 if q in nb0_set else 0))
  163. m1.append((q, fw1_b, h_ix, 1 if q in nb1_set else 0))
  164. m0.sort(key=lambda item: item[1], reverse=True)
  165. m1.sort(key=lambda item: item[1], reverse=True)
  166. MTAB0[a] = tuple(m0)
  167. MTAB1[a] = tuple(m1)
  168. MTAB = (tuple(MTAB0), tuple(MTAB1))
  169.  
  170. # Bảng điểm thưởng khoảng cách tới đích (tự co giãn theo kích thước map)
  171. RT = tuple([0] + [int(950 * (0.60 ** (d - 1))) for d in range(1, MAX_D + 1)])
  172. UNSTOP_RT = tuple([0] + [int(1750 * (0.64 ** (d - 1))) for d in range(1, MAX_D + 1)])
  173.  
  174. rz = random.Random(20260402)
  175. Z = tuple(tuple(rz.getrandbits(52) for _ in range(SIZE)) for _ in range(48))
  176. ZS = rz.getrandbits(52)
  177.  
  178. tabs = (
  179. STRIDE, SIZE, MAX_D, OFFS, DIRN, wall_set, GOAL, NB, DEF_SQ,
  180. DT, MDT, ADV0, ADV1, D0, D1, CENTER, MTAB, RT, UNSTOP_RT, Z, ZS
  181. )
  182. memo['tabs'] = tabs
  183.  
  184. (
  185. STRIDE, SIZE, MAX_D, OFFS, DIRN, wall_set, GOAL, NB, DEF_SQ,
  186. DT, MDT, ADV0, ADV1, D0, D1, CENTER, MTAB, RT, UNSTOP_RT, Z, ZS
  187. ) = tabs
  188.  
  189. # Quản lý ngân sách CPU: tổng 180s/ván, tối đa 1s/nước (chốt ở 0.82s để an toàn)
  190. used = memo.get('used', 0.0)
  191. left = max(1, (maxT - turn) // 2 + 1)
  192. if turn <= 1:
  193. budget = 0.82
  194. else:
  195. budget = min(0.82, max(0.04, (160.0 - used) / min(left, 150)))
  196. deadline = start_time + budget
  197.  
  198. # Dựng bàn cờ 1 chiều: ngoài biên và ô tường đen mang giá trị -1
  199. board = [-1] * SIZE
  200. for yy in range(1, H + 1):
  201. base_y = yy * STRIDE
  202. for xx in range(1, W + 1):
  203. pos = base_y + xx
  204. if pos not in wall_set:
  205. board[pos] = 0
  206.  
  207. # Đánh chỉ số động cho quân hai bên (hỗ trợ map có số quân khác 9)
  208. ally_list = list(gameState.ally)
  209. opp_list = list(gameState.opponent)
  210. na_init = len(ally_list)
  211. nb_init = len(opp_list)
  212.  
  213. ALLY_LO = 1
  214. ALLY_HI = na_init
  215. OPP_LO = na_init + 2
  216. OPP_HI = OPP_LO + nb_init - 1
  217. LO = (ALLY_LO, OPP_LO)
  218. HI = (ALLY_HI, OPP_HI)
  219.  
  220. slot_cap = max(48, OPP_HI + 2)
  221. if len(Z) < slot_cap:
  222. rz = random.Random(20260402)
  223. Z = tuple(tuple(rz.getrandbits(52) for _ in range(SIZE)) for _ in range(slot_cap))
  224.  
  225. tp = [0] * slot_cap
  226. ps = [-1] * slot_cap
  227. pid_of = [0] * slot_cap
  228. TI = {'R': 0, 'P': 1, 'S': 2}
  229. cnt = [0, 0]
  230.  
  231. for idx_a, p in enumerate(ally_list):
  232. c = ALLY_LO + idx_a
  233. pid_of[c] = p.pid
  234. tp[c] = TI[p.Type]
  235. pos = p.y * STRIDE + p.x
  236. ps[c] = pos
  237. board[pos] = c
  238. cnt[0] += 1
  239.  
  240. for idx_b, p in enumerate(opp_list):
  241. c = OPP_LO + idx_b
  242. pid_of[c] = p.pid
  243. tp[c] = TI[p.Type]
  244. pos = p.y * STRIDE + p.x
  245. ps[c] = pos
  246. board[pos] = c
  247. cnt[1] += 1
  248.  
  249. tt = memo.get('tt')
  250. if tt is None or len(tt) > 120000:
  251. tt = {}
  252. memo['tt'] = tt
  253.  
  254. hist_len = SIZE * SIZE
  255. HIST = memo.get('hist')
  256. if HIST is None or len(HIST) != hist_len:
  257. HIST = [0] * hist_len
  258. memo['hist'] = HIST
  259. else:
  260. for i in range(hist_len):
  261. if HIST[i]:
  262. HIST[i] >>= 1
  263.  
  264. KILLER = [None] * 64
  265. nodes = [0]
  266. abort = [False]
  267.  
  268. total = cnt[0] + cnt[1]
  269. if memo.get('prev_total') != total:
  270. memo['prev_total'] = total
  271. memo['capt_turn'] = turn
  272. memo['cfg'] = {}
  273. stag = (turn - memo.get('capt_turn', turn)) // 2
  274. ahead = cnt[0] > cnt[1]
  275. behind = cnt[0] < cnt[1]
  276.  
  277. K = 5 + min(8, turn // 25)
  278. if not behind and stag > 16:
  279. K += min(7, (stag - 16) // 6)
  280. if behind:
  281. HW = 3
  282. elif stag > 16:
  283. HW = 11
  284. else:
  285. HW = 6
  286. if ahead and stag > 26:
  287. DP = (0, 220, 120, 55)
  288. else:
  289. DP = (0, 330, 175, 80)
  290.  
  291. # Phân vai chiến thuật: DEFENDER (chặn đích nhà), RUNNER (đột phá), HUNTER (săn quân)
  292. roles = {}
  293. e_types_alive = [0, 0, 0]
  294. e_near_home = []
  295. e_near_away = [0, 0, 0]
  296. for c in range(OPP_LO, OPP_HI + 1):
  297. q = ps[c]
  298. if q >= 0:
  299. u = tp[c]
  300. e_types_alive[u] += 1
  301. if D1[q] <= 4:
  302. e_near_home.append((D1[q], q, u))
  303. if D0[q] <= 2:
  304. e_near_away[u] += 1
  305. e_near_home.sort()
  306.  
  307. def_assigned = set()
  308. for d_h, q_h, u_h in e_near_home:
  309. need_t = PRED[u_h]
  310. best_c = 0
  311. best_d = 999
  312. for c in range(ALLY_LO, ALLY_HI + 1):
  313. p = ps[c]
  314. if p >= 0 and tp[c] == need_t and c not in def_assigned:
  315. d_c = D1[p]
  316. if d_c < best_d:
  317. best_d = d_c
  318. best_c = c
  319. if best_c:
  320. roles[best_c] = 'DEFENDER'
  321. def_assigned.add(best_c)
  322.  
  323. if not def_assigned:
  324. best_c = 0
  325. best_d = 999
  326. for c in range(ALLY_LO, ALLY_HI + 1):
  327. p = ps[c]
  328. if p >= 0 and e_types_alive[PREY[tp[c]]] > 0:
  329. d_c = D1[p]
  330. if d_c < best_d:
  331. best_d = d_c
  332. best_c = c
  333. if best_c and best_d <= 3:
  334. roles[best_c] = 'DEFENDER'
  335. def_assigned.add(best_c)
  336.  
  337. runner_cands = []
  338. for c in range(ALLY_LO, ALLY_HI + 1):
  339. p = ps[c]
  340. if p >= 0 and c not in def_assigned:
  341. t = tp[c]
  342. imm = 1 if e_types_alive[PRED[t]] == 0 else 0
  343. brk = 1 if e_near_away[PREY[t]] > 0 else 0
  344. runner_cands.append((D0[p] - 2 * imm - brk, -ADV0[p], c))
  345. runner_cands.sort()
  346. num_runners = max(2, len(GOAL[0])) if goal_mode == 1 else 2
  347. for idx_r in range(min(num_runners, len(runner_cands))):
  348. roles[runner_cands[idx_r][2]] = 'RUNNER'
  349. for c in range(ALLY_LO, ALLY_HI + 1):
  350. if ps[c] >= 0 and c not in roles:
  351. roles[c] = 'HUNTER'
  352.  
  353. # Kiểm tra phe s có thể chạm đích thắng ngay trong 1 nước đi không
  354. def wins_now(s):
  355. lo = LO[s]
  356. hi = HI[s]
  357. gset = GOAL[s]
  358. if goal_mode == 0:
  359. for g in gset:
  360. v0 = board[g]
  361. if lo <= v0 <= hi:
  362. return True
  363. if v0 == 0:
  364. for off in OFFS:
  365. v = board[g + off]
  366. if lo <= v <= hi:
  367. return True
  368. else:
  369. t = tp[v0]
  370. for off in OFFS:
  371. v = board[g + off]
  372. if lo <= v <= hi and BT[tp[v]][t]:
  373. return True
  374. return False
  375. else:
  376. missing = [g for g in gset if not (lo <= board[g] <= hi)]
  377. if not missing:
  378. return True
  379. if len(missing) > 1:
  380. return False
  381. g = missing[0]
  382. v0 = board[g]
  383. for off in OFFS:
  384. src = g + off
  385. v = board[src]
  386. if lo <= v <= hi and src not in gset:
  387. if v0 == 0 or BT[tp[v]][tp[v0]]:
  388. return True
  389. return False
  390.  
  391. # Sinh nước đi hợp lệ: mode=0 (tất cả), mode=1 (chỉ ăn quân), mode=2 (ăn quân + áp sát đích)
  392. def gen(s, mode):
  393. res = []
  394. lo = LO[s]
  395. hi = HI[s]
  396. elo = LO[1 - s]
  397. ehi = HI[1 - s]
  398. mtab = MTAB[s]
  399. gset = GOAL[s]
  400. egset = GOAL[1 - s]
  401. for c in range(lo, hi + 1):
  402. p = ps[c]
  403. if p < 0:
  404. continue
  405. t = tp[c]
  406. bt = BT[t]
  407. on_goal = (goal_mode == 1 and p in gset)
  408. for q, fw_b, h_ix, is_nb in mtab[p]:
  409. v = board[q]
  410. if v == 0:
  411. if mode == 0:
  412. h = HIST[h_ix]
  413. pen_leave = -35 if (on_goal and q not in gset) else 0
  414. res.append((fw_b + pen_leave + (h if h < 72 else 72), c, p, q, 0))
  415. elif mode == 2 and is_nb:
  416. for off in OFFS:
  417. gq = q + off
  418. if gq in gset:
  419. vg = board[gq]
  420. if vg == 0 or (elo <= vg <= ehi and bt[tp[vg]]):
  421. res.append((820 + fw_b, c, p, q, 0))
  422. break
  423. elif elo <= v <= ehi:
  424. if bt[tp[v]]:
  425. bonus = 90 if (q in egset or D1[q] <= 2) else 0
  426. res.append((1050 + fw_b + bonus, c, p, q, v))
  427. return res
  428.  
  429. # Hàm lượng giá thế cờ tĩnh từ góc nhìn phe s
  430. def evaluate_raw(s):
  431. A_p = []
  432. A_t = []
  433. B_p = []
  434. B_t = []
  435. cA0 = cA1 = cA2 = 0
  436. cB0 = cB1 = cB2 = 0
  437. for c in range(ALLY_LO, ALLY_HI + 1):
  438. p = ps[c]
  439. if p >= 0:
  440. t = tp[c]
  441. A_p.append(p)
  442. A_t.append(t)
  443. if t == 0:
  444. cA0 += 1
  445. elif t == 1:
  446. cA1 += 1
  447. else:
  448. cA2 += 1
  449. for c in range(OPP_LO, OPP_HI + 1):
  450. p = ps[c]
  451. if p >= 0:
  452. t = tp[c]
  453. B_p.append(p)
  454. B_t.append(t)
  455. if t == 0:
  456. cB0 += 1
  457. elif t == 1:
  458. cB1 += 1
  459. else:
  460. cB2 += 1
  461.  
  462. na = len(A_p)
  463. nb = len(B_p)
  464. if na == 0:
  465. return -WIN + 100 if s == 0 else WIN - 100
  466. if nb == 0:
  467. return WIN - 100 if s == 0 else -WIN + 100
  468.  
  469. cA = (cA0, cA1, cA2)
  470. cB = (cB0, cB1, cB2)
  471.  
  472. # Hơn quân và lợi thế tuyệt đối khi địch mất sạch loại quân khắc chế
  473. v = 1050 * (na - nb) + 3200 * (na - nb) // (na + nb)
  474.  
  475. for t in (0, 1, 2):
  476. pred = PRED[t]
  477. prey = PREY[t]
  478. ca = cA[t]
  479. cb = cB[t]
  480. if ca:
  481. if cB[pred] == 0:
  482. v += ca * (500 if cB[t] == 0 else 360)
  483. elif cB[pred] == 1 and cA[prey] > 0:
  484. v += ca * ELIM1
  485. elif ELIM2 and cB[pred] == 2 and cA[prey] >= 2:
  486. v += ca * ELIM2
  487. v += ca * (80 * cB[prey] if cB[prey] else -50)
  488. if cb:
  489. if cA[pred] == 0:
  490. v -= cb * (500 if cA[t] == 0 else 360)
  491. elif cA[pred] == 1 and cB[prey] > 0:
  492. v -= cb * ELIM1
  493. elif ELIM2 and cA[pred] == 2 and cB[prey] >= 2:
  494. v -= cb * ELIM2
  495. v -= cb * (80 * cA[prey] if cA[prey] else -50)
  496.  
  497. # Điểm chiếm đóng đồng thời khi bản đồ bật goalMode == 1
  498. if goal_mode == 1:
  499. occ0 = 0
  500. for g in GOAL[0]:
  501. if ALLY_LO <= board[g] <= ALLY_HI:
  502. occ0 += 1
  503. occ1 = 0
  504. for g in GOAL[1]:
  505. if OPP_LO <= board[g] <= OPP_HI:
  506. occ1 += 1
  507. if occ0 == len(GOAL[0]):
  508. return WIN - 50 if s == 0 else -WIN + 50
  509. if occ1 == len(GOAL[1]):
  510. return -WIN + 50 if s == 0 else WIN - 50
  511. v += 900 * (occ0 - occ1)
  512.  
  513. ha = [MAX_D] * na
  514. pa = [MAX_D] * na
  515. ma = [MAX_D * 2] * na
  516. inta = [999] * na
  517. hb = [MAX_D] * nb
  518. pb = [MAX_D] * nb
  519. mb = [MAX_D * 2] * nb
  520. intb = [999] * nb
  521.  
  522. for i in range(na):
  523. p = A_p[i]
  524. t = A_t[i]
  525. d0p = D0[p]
  526. row = DT[p]
  527. mrow = MDT[p]
  528. bt = BT[t]
  529. for j in range(nb):
  530. q = B_p[j]
  531. u = B_t[j]
  532. d0q = D0[q]
  533. d1q = D1[q]
  534. if u == t:
  535. if d0q <= d0p and d0q < inta[i]:
  536. inta[i] = d0q
  537. if D1[p] <= d1q and D1[p] < intb[j]:
  538. intb[j] = D1[p]
  539. continue
  540. d = row[q]
  541. md = mrow[q]
  542. if bt[u]:
  543. if d < pa[i]:
  544. pa[i] = d
  545. if md < ma[i]:
  546. ma[i] = md
  547. if d < hb[j]:
  548. hb[j] = d
  549. if D1[p] <= d1q + 1 and D1[p] < intb[j]:
  550. intb[j] = D1[p]
  551. else:
  552. if d < ha[i]:
  553. ha[i] = d
  554. if d < pb[j]:
  555. pb[j] = d
  556. if md < mb[j]:
  557. mb[j] = md
  558. if d0q <= d0p + 1 and d0q < inta[i]:
  559. inta[i] = d0q
  560.  
  561. adv0 = 0
  562. thr0 = 0
  563. dg0 = 0
  564. hunt0 = 0
  565. hang0 = 0
  566. away_guard_b = [0, 0, 0]
  567. for j in range(nb):
  568. if D0[B_p[j]] <= 2:
  569. away_guard_b[B_t[j]] = 1
  570.  
  571. max_unstop = len(UNSTOP_RT) - 1
  572. max_rt = len(RT) - 1
  573. hunt_range = max(10, (W + H) // 2 + 2)
  574.  
  575. for i in range(na):
  576. p = A_p[i]
  577. t = A_t[i]
  578. ap = ADV0[p]
  579. if cB[PRED[t]] == 0:
  580. ap += ap >> 1
  581. if away_guard_b[PREY[t]]:
  582. ap += max(0, hunt_range - D0[p]) * 3
  583. adv0 += ap
  584.  
  585. mp = pa[i]
  586. if mp < MAX_D:
  587. hunt0 += HW * max(0, hunt_range - mp) + max(0, hunt_range * 2 - ma[i])
  588. else:
  589. hunt0 += CENTER[p]
  590.  
  591. mh = ha[i]
  592. if mh <= 3:
  593. pt = PREY[t]
  594. row = DT[p]
  595. prot = False
  596. for k in range(na):
  597. if A_t[k] == pt and row[A_p[k]] <= 1:
  598. prot = True
  599. break
  600. if not prot:
  601. if mh <= 1:
  602. dg0 += DP[1] + (160 if s == 1 else 0)
  603. hang0 += 1
  604. else:
  605. dg0 += DP[mh]
  606. elif mh <= 2:
  607. hunt0 += 25
  608.  
  609. d = D0[p]
  610. if inta[i] == 999:
  611. if d <= max_unstop:
  612. tv = UNSTOP_RT[d]
  613. if tv > thr0:
  614. thr0 = tv
  615. elif d <= max_rt:
  616. tv = RT[d]
  617. if mh <= 2 or inta[i] <= d:
  618. tv //= 3
  619. if tv > thr0:
  620. thr0 = tv
  621.  
  622. if hang0 >= 2:
  623. dg0 += 380 * (hang0 - 1)
  624.  
  625. adv1 = 0
  626. thr1 = 0
  627. dg1 = 0
  628. hunt1 = 0
  629. hang1 = 0
  630. home_guard_a = [0, 0, 0]
  631. for i in range(na):
  632. if D1[A_p[i]] <= 2:
  633. home_guard_a[A_t[i]] = 1
  634.  
  635. for j in range(nb):
  636. q = B_p[j]
  637. u = B_t[j]
  638. aq = ADV1[q]
  639. if cA[PRED[u]] == 0:
  640. aq += aq >> 1
  641. if home_guard_a[PREY[u]]:
  642. aq += max(0, hunt_range - D1[q]) * 3
  643. adv1 += aq
  644.  
  645. mp = pb[j]
  646. if mp < MAX_D:
  647. hunt1 += HW * max(0, hunt_range - mp) + max(0, hunt_range * 2 - mb[j])
  648. else:
  649. hunt1 += CENTER[q]
  650.  
  651. mh = hb[j]
  652. if mh <= 3:
  653. pt = PREY[u]
  654. row = DT[q]
  655. prot = False
  656. for k in range(nb):
  657. if B_t[k] == pt and row[B_p[k]] <= 1:
  658. prot = True
  659. break
  660. if not prot:
  661. if mh <= 1:
  662. dg1 += DP[1] + (160 if s == 0 else 0)
  663. hang1 += 1
  664. else:
  665. dg1 += DP[mh]
  666. elif mh <= 2:
  667. hunt1 += 25
  668.  
  669. d = D1[q]
  670. if intb[j] == 999:
  671. if d <= max_unstop:
  672. tv = UNSTOP_RT[d]
  673. if tv > thr1:
  674. thr1 = tv
  675. elif d <= max_rt:
  676. tv = RT[d]
  677. if mh <= 2 or intb[j] <= d:
  678. tv //= 3
  679. if tv > thr1:
  680. thr1 = tv
  681.  
  682. if hang1 >= 2:
  683. dg1 += 380 * (hang1 - 1)
  684.  
  685. v += K * (adv0 - adv1) + (thr0 - thr1) - (dg0 - dg1) + (hunt0 - hunt1)
  686.  
  687. # Phòng thủ vùng cờ nhà
  688. has_useful_def = False
  689. for i in range(na):
  690. if D1[A_p[i]] <= 2 and cB[PREY[A_t[i]]] > 0:
  691. has_useful_def = True
  692. break
  693.  
  694. near_cnt = 0
  695. for j in range(nb):
  696. d = D1[B_p[j]]
  697. if d <= 2:
  698. v -= 520 * (3 - d)
  699. near_cnt += 1
  700. if intb[j] == 999:
  701. v -= 450 * (3 - d)
  702. elif d == 3:
  703. v -= 110
  704. if intb[j] == 999:
  705. v -= 220
  706. elif d == 4:
  707. v -= 35
  708.  
  709. if near_cnt >= 2:
  710. v -= 420 * (near_cnt - 1)
  711. if near_cnt >= 1 and not has_useful_def:
  712. v -= 420 * near_cnt
  713. if has_useful_def:
  714. v += 135 + 105 * near_cnt
  715.  
  716. return v if s == 0 else -v
  717.  
  718. evc = {}
  719.  
  720. def evaluate(s, h):
  721. r = evc.get(h)
  722. if r is None:
  723. r = evaluate_raw(s)
  724. evc[h] = r
  725. return r
  726.  
  727. # Tìm kiếm tĩnh (Quiescence Search) giải quyết chuỗi ăn quân và đe dọa chạm đích
  728. def qsearch(s, alpha, beta, ply, qd, h):
  729. nodes[0] += 1
  730. if (nodes[0] & 63) == 0 and (time.process_time() > deadline or time.process_time() - start_time > 0.84):
  731. abort[0] = True
  732. if abort[0]:
  733. return 0
  734. if wins_now(s):
  735. return WIN - ply
  736. o = 1 - s
  737. in_chk = wins_now(o)
  738. if in_chk:
  739. if qd >= 4:
  740. return -WIN + ply + 1
  741. all_m = gen(s, 0)
  742. def_sq = DEF_SQ[o]
  743. evasions = [m for m in all_m if m[3] in def_sq]
  744. if not evasions:
  745. return -WIN + ply + 1
  746. evasions.sort(reverse=True)
  747. best = -WIN + ply + 1
  748. for m in evasions:
  749. c = m[1]
  750. p = m[2]
  751. q = m[3]
  752. cap = m[4]
  753. board[p] = 0
  754. board[q] = c
  755. ps[c] = q
  756. h2 = h ^ Z[c][p] ^ Z[c][q] ^ ZS
  757. if cap:
  758. ps[cap] = -1
  759. cnt[o] -= 1
  760. h2 ^= Z[cap][q]
  761. still_w = wins_now(o)
  762. if not still_w:
  763. if cap and cnt[o] == 0:
  764. val = WIN - ply
  765. else:
  766. val = -qsearch(o, -beta, -alpha, ply + 1, qd + 1, h2)
  767. else:
  768. val = -WIN + ply + 1
  769. if cap:
  770. cnt[o] += 1
  771. ps[cap] = q
  772. board[q] = cap
  773. board[p] = c
  774. ps[c] = p
  775. if abort[0]:
  776. return 0
  777. if val > best:
  778. best = val
  779. if val > alpha:
  780. alpha = val
  781. if alpha >= beta:
  782. break
  783. return best
  784.  
  785. stand = evaluate(s, h)
  786. if stand >= beta:
  787. return stand
  788. if stand > alpha:
  789. alpha = stand
  790. if qd >= 6:
  791. return stand
  792.  
  793. moves = gen(s, 2 if qd == 0 else 1)
  794. if not moves:
  795. return stand
  796. moves.sort(reverse=True)
  797. for m in moves:
  798. c = m[1]
  799. p = m[2]
  800. q = m[3]
  801. cap = m[4]
  802. board[p] = 0
  803. board[q] = c
  804. ps[c] = q
  805. h2 = h ^ Z[c][p] ^ Z[c][q] ^ ZS
  806. if cap:
  807. ps[cap] = -1
  808. cnt[o] -= 1
  809. h2 ^= Z[cap][q]
  810. if cap and cnt[o] == 0:
  811. val = WIN - ply
  812. else:
  813. val = -qsearch(o, -beta, -alpha, ply + 1, qd + 1, h2)
  814. if cap:
  815. cnt[o] += 1
  816. ps[cap] = q
  817. board[q] = cap
  818. board[p] = c
  819. ps[c] = p
  820. if abort[0]:
  821. return 0
  822. if val > stand:
  823. stand = val
  824. if val > alpha:
  825. alpha = val
  826. if alpha >= beta:
  827. break
  828. return stand
  829.  
  830. # Alpha-Beta PVS kết hợp bảng băm TT, Killer Heuristic và Late Move Reduction
  831. def search(s, depth, alpha, beta, ply, h):
  832. nodes[0] += 1
  833. if (nodes[0] & 63) == 0 and (time.process_time() > deadline or time.process_time() - start_time > 0.84):
  834. abort[0] = True
  835. if abort[0]:
  836. return 0
  837. if wins_now(s):
  838. return WIN - ply
  839. o = 1 - s
  840. in_chk = wins_now(o)
  841. if depth <= 0:
  842. if in_chk and ply < PLYLIM:
  843. depth = 1
  844. else:
  845. return qsearch(s, alpha, beta, ply, 0, h)
  846. a0 = alpha
  847. ttm = None
  848. e = tt.get(h)
  849. if e is not None:
  850. ed, ef, ev, em = e
  851. if ev > WIN - 1000:
  852. ev -= ply
  853. elif ev < -WIN + 1000:
  854. ev += ply
  855. ttm = em
  856. if ed >= depth:
  857. if ef == 0:
  858. return ev
  859. if ef == 1:
  860. if ev >= beta:
  861. return ev
  862. elif ev <= alpha:
  863. return ev
  864.  
  865. moves = gen(s, 0)
  866. if not moves:
  867. return -WIN + ply + 1 if in_chk else evaluate(s, h)
  868. if in_chk:
  869. def_sq = DEF_SQ[o]
  870. moves = [m for m in moves if m[3] in def_sq]
  871. if not moves:
  872. return -WIN + ply + 1
  873.  
  874. moves.sort(reverse=True)
  875. km = KILLER[ply] if ply < 64 else None
  876. if km is not None or ttm is not None:
  877. for i in range(len(moves)):
  878. m = moves[i]
  879. if ttm is not None and m[1] == ttm[0] and m[2] == ttm[1] and m[3] == ttm[2]:
  880. if i:
  881. moves.insert(0, moves.pop(i))
  882. break
  883. if km is not None:
  884. for i in range(1, len(moves)):
  885. m = moves[i]
  886. if m[1] == km[0] and m[2] == km[1] and m[3] == km[2]:
  887. moves.insert(1 if ttm is not None else 0, moves.pop(i))
  888. break
  889.  
  890. best = -WIN + ply + 1 if in_chk else -INF
  891. bm = None
  892. idx = 0
  893. nb_set = DEF_SQ[s]
  894. for m in moves:
  895. c = m[1]
  896. p = m[2]
  897. q = m[3]
  898. cap = m[4]
  899. board[p] = 0
  900. board[q] = c
  901. ps[c] = q
  902. h2 = h ^ Z[c][p] ^ Z[c][q] ^ ZS
  903. if cap:
  904. ps[cap] = -1
  905. cnt[o] -= 1
  906. h2 ^= Z[cap][q]
  907.  
  908. if in_chk and wins_now(o):
  909. val = -WIN + ply + 1
  910. elif cap and cnt[o] == 0:
  911. val = WIN - ply
  912. else:
  913. if idx == 0:
  914. val = -search(o, depth - 1, -beta, -alpha, ply + 1, h2)
  915. else:
  916. red = 1 if (idx >= 4 and depth >= 3 and not cap and not in_chk and q not in nb_set) else 0
  917. if idx >= 12 and depth >= 5 and red:
  918. red = 2
  919. val = -search(o, depth - 1 - red, -alpha - 1, -alpha, ply + 1, h2)
  920. if val > alpha and not abort[0]:
  921. val = -search(o, depth - 1, -beta, -alpha, ply + 1, h2)
  922.  
  923. if cap:
  924. cnt[o] += 1
  925. ps[cap] = q
  926. board[q] = cap
  927. board[p] = c
  928. ps[c] = p
  929. if abort[0]:
  930. return 0
  931. if val > best:
  932. best = val
  933. bm = (c, p, q)
  934. if val > alpha:
  935. alpha = val
  936. if alpha >= beta:
  937. if not cap:
  938. HIST[p * SIZE + q] += depth * depth
  939. if ply < 64:
  940. KILLER[ply] = bm
  941. break
  942. idx += 1
  943.  
  944. sv = best
  945. if sv > WIN - 1000:
  946. sv += ply
  947. elif sv < -WIN + 1000:
  948. sv -= ply
  949. if best <= a0:
  950. fl = 2
  951. elif best >= beta:
  952. fl = 1
  953. else:
  954. fl = 0
  955. tt[h] = (depth, fl, sv, bm)
  956. return best
  957.  
  958. # Chọn nước đi tối ưu bằng Iterative Deepening tại nút gốc
  959. def decide():
  960. root = gen(0, 0)
  961. if not root:
  962. return (0, 0)
  963.  
  964. # Kiểm tra nước thắng ngay lập tức
  965. gset0 = GOAL[0]
  966. for m in root:
  967. c0 = m[1]
  968. p0 = m[2]
  969. q0 = m[3]
  970. cap0 = m[4]
  971. if cap0 and cnt[1] == 1:
  972. return (pid_of[c0], DIRN[q0 - p0])
  973. if q0 in gset0:
  974. if goal_mode == 0:
  975. return (pid_of[c0], DIRN[q0 - p0])
  976. elif all((g == q0 or (ALLY_LO <= board[g] <= ALLY_HI and g != p0)) for g in gset0):
  977. return (pid_of[c0], DIRN[q0 - p0])
  978.  
  979. # Nếu địch đang dọa chạm đích ở lượt kế, lọc các nước phòng thủ bắt buộc
  980. if wins_now(1):
  981. safe = []
  982. for m in root:
  983. c0 = m[1]
  984. p0 = m[2]
  985. q0 = m[3]
  986. cap0 = m[4]
  987. board[p0] = 0
  988. board[q0] = c0
  989. ps[c0] = q0
  990. if cap0:
  991. ps[cap0] = -1
  992. cnt[1] -= 1
  993. if not wins_now(1):
  994. safe.append(m)
  995. if cap0:
  996. cnt[1] += 1
  997. ps[cap0] = q0
  998. board[q0] = cap0
  999. board[p0] = c0
  1000. ps[c0] = p0
  1001. if safe:
  1002. root = safe
  1003.  
  1004. if len(root) == 1:
  1005. return (pid_of[root[0][1]], DIRN[root[0][3] - root[0][2]])
  1006.  
  1007. h0 = 0
  1008. for c in range(1, OPP_HI + 1):
  1009. if ps[c] >= 0:
  1010. h0 ^= Z[c][ps[c]]
  1011.  
  1012. seen = memo.setdefault('seen', {})
  1013. cfg = memo.setdefault('cfg', {})
  1014. pen_unit = 130 + 2 * min(turn, 200)
  1015. cfg_unit = (45 + 4 * stag) * (0.25 if behind else 1.0)
  1016. hunt_range = max(10, (W + H) // 2 + 2)
  1017.  
  1018. order = []
  1019. for m in root:
  1020. c = m[1]
  1021. p = m[2]
  1022. q = m[3]
  1023. cap = m[4]
  1024. t = tp[c]
  1025. pred_t = PRED[t]
  1026. prey_t = PREY[t]
  1027. role = roles.get(c, 'HUNTER')
  1028. tb = m[0] * 10
  1029. if cap:
  1030. tb += 5000
  1031.  
  1032. min_pred_p = 999
  1033. min_pred_q = 999
  1034. min_prey_q = 999
  1035. row_p = DT[p]
  1036. row_q = DT[q]
  1037. for ec in range(OPP_LO, OPP_HI + 1):
  1038. ep = ps[ec]
  1039. if ep >= 0 and ec != cap:
  1040. et = tp[ec]
  1041. if et == pred_t:
  1042. if row_p[ep] < min_pred_p:
  1043. min_pred_p = row_p[ep]
  1044. if row_q[ep] < min_pred_q:
  1045. min_pred_q = row_q[ep]
  1046. elif et == prey_t:
  1047. if row_q[ep] < min_prey_q:
  1048. min_prey_q = row_q[ep]
  1049.  
  1050. if min_pred_p == 1 and min_pred_q > 1:
  1051. tb += 350
  1052. if min_pred_q == 1:
  1053. tb -= 600
  1054.  
  1055. if role == 'RUNNER':
  1056. tb += (D0[p] - D0[q]) * 45 + (ADV0[q] - ADV0[p]) * 20
  1057. elif role == 'DEFENDER':
  1058. tb += (D1[p] - D1[q]) * 35
  1059. else:
  1060. tb += max(0, hunt_range - min_prey_q) * 25
  1061. order.append((tb, m))
  1062. order.sort(reverse=True)
  1063. order = [x[1] for x in order]
  1064.  
  1065. # Phạt nhẹ các thế cờ lặp lại để tránh đi quẩn quanh
  1066. pens = {}
  1067. for m in order:
  1068. c = m[1]
  1069. p = m[2]
  1070. q = m[3]
  1071. cap = m[4]
  1072. board[p] = 0
  1073. board[q] = c
  1074. ps[c] = q
  1075. if cap:
  1076. ps[cap] = -1
  1077. key = tuple(ps)
  1078. ck = tuple(ps[ALLY_LO:ALLY_HI + 1])
  1079. if cap:
  1080. ps[cap] = q
  1081. board[q] = cap
  1082. board[p] = c
  1083. ps[c] = p
  1084. pens[(c, p, q)] = min(750, int(pen_unit * seen.get(key, 0) + cfg_unit * cfg.get(ck, 0)))
  1085.  
  1086. best_move = order[0]
  1087. for depth in range(1, 42):
  1088. best_adj = -INF
  1089. cur_best = None
  1090. done = 0
  1091. vals = []
  1092. for m in order:
  1093. if time.process_time() - start_time > 0.84:
  1094. abort[0] = True
  1095. break
  1096. c = m[1]
  1097. p = m[2]
  1098. q = m[3]
  1099. cap = m[4]
  1100. pen = pens[(c, p, q)]
  1101. araw = best_adj + pen if best_adj > -INF else -INF
  1102. board[p] = 0
  1103. board[q] = c
  1104. ps[c] = q
  1105. h2 = h0 ^ Z[c][p] ^ Z[c][q] ^ ZS
  1106. if cap:
  1107. ps[cap] = -1
  1108. cnt[1] -= 1
  1109. h2 ^= Z[cap][q]
  1110. val = -search(1, depth - 1, -INF, -araw if araw > -INF else INF, 1, h2)
  1111. if cap:
  1112. cnt[1] += 1
  1113. ps[cap] = q
  1114. board[q] = cap
  1115. board[p] = c
  1116. ps[c] = p
  1117. if abort[0]:
  1118. break
  1119. done += 1
  1120. adj = val - pen if val < WIN - 1000 else val
  1121. vals.append((adj, m))
  1122. if adj > best_adj:
  1123. best_adj = adj
  1124. cur_best = m
  1125.  
  1126. if cur_best is not None and (not abort[0] or done >= 1):
  1127. best_move = cur_best
  1128. if not abort[0]:
  1129. vals.sort(key=lambda z: z[0], reverse=True)
  1130. order = [t_item[1] for t_item in vals]
  1131. else:
  1132. order.remove(cur_best)
  1133. order.insert(0, cur_best)
  1134. if abort[0]:
  1135. break
  1136. if best_adj > WIN - 1000 or best_adj < -WIN + 1000:
  1137. break
  1138. if time.process_time() > deadline or time.process_time() - start_time > 0.84:
  1139. break
  1140.  
  1141. c = best_move[1]
  1142. p = best_move[2]
  1143. q = best_move[3]
  1144. cap = best_move[4]
  1145. board[p] = 0
  1146. board[q] = c
  1147. ps[c] = q
  1148. if cap:
  1149. ps[cap] = -1
  1150. key = tuple(ps)
  1151. seen[key] = seen.get(key, 0) + 1
  1152. ck = tuple(ps[ALLY_LO:ALLY_HI + 1])
  1153. cfg[ck] = cfg.get(ck, 0) + 1
  1154. return (pid_of[c], DIRN[q - p])
  1155.  
  1156. try:
  1157. res = decide()
  1158. except Exception:
  1159. # Nhánh dự phòng an toàn nếu gặp sự cố bất ngờ
  1160. res = (0, 0)
  1161. bestk = -INF
  1162. dirs = ((-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0))
  1163. for p in ally_list:
  1164. for d in range(1, 9):
  1165. dx, dy = dirs[d - 1]
  1166. nx = p.x + dx
  1167. ny = p.y + dy
  1168. if nx < 1 or nx > W or ny < 1 or ny > H or (nx, ny) in walls_xy:
  1169. continue
  1170. ok = True
  1171. for a in ally_list:
  1172. if a.x == nx and a.y == ny:
  1173. ok = False
  1174. for e in opp_list:
  1175. if e.x == nx and e.y == ny:
  1176. if not ((p.Type == 'R' and e.Type == 'S') or (p.Type == 'S' and e.Type == 'P') or (p.Type == 'P' and e.Type == 'R')):
  1177. ok = False
  1178. if ok and ADV0[ny * STRIDE + nx] > bestk:
  1179. bestk = ADV0[ny * STRIDE + nx]
  1180. res = (p.pid, d)
  1181.  
  1182. memo['used'] = used + (time.process_time() - start_time)
  1183. return res
Success #stdin #stdout 0.07s 14392KB
stdin
Standard input is empty
stdout
Standard output is empty