parse.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/env python3
  2. import time
  3. COLUMNS = 7
  4. BLOCK_SIZE = 60
  5. MAX_COLUMN_HEIGHT = 546
  6. DETECT_COLUMN_OFFSET_X = 9, 49
  7. DETECT_COLUMN_OFFSET_Y = 11
  8. MIN_COLUMN_SAT = 130
  9. MIN_COLUMN_VAL = 120
  10. COLUMN_VSHIFT = [1, 1, 1, 0, 0, 0, 0]
  11. #COLUMN_VSHIFT = [2, 2, 1, 1, 0, 0, 0]
  12. RED, PINK, GREEN, BLUE, YELLOW, NOBLOCK = range(6)
  13. BOMB_OFFSET = NOBLOCK + 1
  14. BASIC_HUES = [248, 224, 118, 158, 26]
  15. BOMB_HUES = [250, 219, 132, 174, 38]
  16. HUE_TOLERANCE = 5
  17. DETECT_BASIC_X = 9
  18. DETECT_BASIC_Y = 15
  19. DETECT_BOMB_X = 22
  20. DETECT_BOMB_Y = 43
  21. DETECT_BOMB_Y_TOLERANCE = 7
  22. DETECT_BOMB_CHECK = 8, 20, 137, 185, 9 # x, y, hue, min_value, y_tolerance
  23. MIN_BASIC_SAT = 180
  24. MIN_BOMB_SAT = 130
  25. DETECT_EXA_X = 30
  26. DETECT_EXA_Y = 547
  27. EXA_HUE = 129
  28. EXA_MIN_VAL = 194
  29. DETECT_HELD_Y = 579
  30. DETECT_EXA_LIGHT_X = 30
  31. DETECT_EXA_LIGHT_Y = 609
  32. EXA_LIGHT_HUE = 226
  33. MIN_EXA_LIGHT_VAL = 172
  34. def is_hue(h, hexpect):
  35. return abs(h - hexpect) <= HUE_TOLERANCE
  36. def is_basic(block):
  37. return RED <= block <= YELLOW
  38. def is_bomb(block):
  39. return block > NOBLOCK
  40. def bomb_to_basic(block):
  41. assert is_bomb(block)
  42. return block - BOMB_OFFSET
  43. def detect_columns(board):
  44. def saturated(x, y):
  45. h, s, v = board.getpixel((x, y))
  46. return s > MIN_COLUMN_SAT and v > MIN_COLUMN_VAL
  47. miny = -min(DETECT_BASIC_Y, DETECT_BOMB_Y)
  48. for y in range(MAX_COLUMN_HEIGHT, miny + BLOCK_SIZE, -1):
  49. for col in range(COLUMNS):
  50. x = col * BLOCK_SIZE
  51. if all(saturated(x + cx, y) for cx in DETECT_COLUMN_OFFSET_X):
  52. #print('found bottom in column', col)
  53. return y + 1 + DETECT_COLUMN_OFFSET_Y - COLUMN_VSHIFT[col]
  54. def detect_block_type(board, x, y):
  55. # check for basic blocks first, use saturation filter to avoid confusing
  56. # green blocks with background
  57. h, s, v = board.getpixel((x + DETECT_BASIC_X,
  58. y + DETECT_BASIC_Y))
  59. if s >= MIN_BASIC_SAT:
  60. for ty, hexpect in enumerate(BASIC_HUES):
  61. if is_hue(h, hexpect):
  62. return ty
  63. # if no basic block is detected, check another range of pixels for bomb
  64. # contents
  65. def check_bomb():
  66. dx, dy, hexpect, minval, tolerance = DETECT_BOMB_CHECK
  67. for offset in range(tolerance):
  68. h, s, v = board.getpixel((x + dx, y + dy + offset))
  69. if is_hue(h, hexpect) and v >= minval:
  70. return True
  71. return False
  72. if check_bomb():
  73. for offset in range(DETECT_BOMB_Y_TOLERANCE):
  74. h, s, v = board.getpixel((x + DETECT_BOMB_X,
  75. y + DETECT_BOMB_Y + offset))
  76. if s >= MIN_BOMB_SAT:
  77. for basic_ty, hexpect in enumerate(BOMB_HUES):
  78. if is_hue(h, hexpect):
  79. return basic_ty + BOMB_OFFSET
  80. # no basic block or bomb -> empty slot
  81. return NOBLOCK
  82. def detect_blocks(board):
  83. maxy = detect_columns(board) - BLOCK_SIZE
  84. miny = -min(DETECT_BASIC_Y, DETECT_BOMB_Y)
  85. for y in range(maxy, miny, -BLOCK_SIZE):
  86. for col in range(COLUMNS):
  87. x = col * BLOCK_SIZE
  88. yield detect_block_type(board, x, y + COLUMN_VSHIFT[col])
  89. def detect_exa(board):
  90. for col in range(COLUMNS):
  91. x = col * BLOCK_SIZE + DETECT_EXA_X
  92. y = DETECT_EXA_Y + COLUMN_VSHIFT[col]
  93. h, s, v = board.getpixel((x, y))
  94. if is_hue(h, EXA_HUE) and v >= EXA_MIN_VAL:
  95. return col
  96. def detect_held(board, exa):
  97. if exa is not None:
  98. x = exa * BLOCK_SIZE + DETECT_EXA_LIGHT_X
  99. y = DETECT_EXA_LIGHT_Y + COLUMN_VSHIFT[exa]
  100. h, s, v = board.getpixel((x, y))
  101. if not is_hue(h, EXA_LIGHT_HUE) or v < MIN_EXA_LIGHT_VAL:
  102. return detect_block_type(board, exa * BLOCK_SIZE, DETECT_HELD_Y)
  103. return NOBLOCK
  104. def print_board(blocks, exa, held):
  105. rows = len(blocks) // COLUMNS
  106. for row in range(rows):
  107. row = rows - row
  108. row_blocks = blocks[(row - 1) * COLUMNS:row * COLUMNS]
  109. print(' ' + ''.join('rpgby.RPGBY'[ty] for ty in row_blocks))
  110. if exa is not None:
  111. print(' ' * exa + ' |')
  112. print(' ' * int(exa > 0), '-' * (exa - 1),
  113. '(', 'rpgby RPGBY'[held], ')',
  114. '-' * (COLUMNS - exa - 2), sep='')
  115. if __name__ == '__main__':
  116. from interaction import get_exapunks_window, screenshot_board
  117. win = get_exapunks_window()
  118. win.raise_window()
  119. while True:
  120. board = screenshot_board(win)
  121. blocks = list(detect_blocks(board))
  122. exa = detect_exa(board)
  123. held = detect_held(board, exa)
  124. print('\033c', end='')
  125. print_board(blocks, exa, held)
  126. time.sleep(0.05)