coords.py 3.89 KB
Newer Older
Yanhui Liang's avatar
Yanhui Liang committed
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
# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Logic for dealing with coordinates.

This introduces some helpers and terminology that are used throughout MiniGo.

MiniGo Coordinate: This is a tuple of the form (row, column) that is indexed
  starting out at (0, 0) from the upper-left.
Flattened Coordinate: this is a number ranging from 0 - N^2 (so N^2+1
  possible values). The extra value N^2 is used to mark a 'pass' move.
SGF Coordinate: Coordinate used for SGF serialization format. Coordinates use
  two-letter pairs having the form (column, row) indexed from the upper-left
  where 0, 0 = 'aa'.
KGS Coordinate: Human-readable coordinate string indexed from bottom left, with
  the first character a capital letter for the column and the second a number
  from 1-19 for the row. Note that KGS chooses to skip the letter 'I' due to
  its similarity with 'l' (lowercase 'L').
PYGTP Coordinate: Tuple coordinate indexed starting at 1,1 from bottom-left
  in the format (column, row)

So, for a 19x19,

Coord Type      upper_left      upper_right     pass
-------------------------------------------------------
minigo coord    (0, 0)          (0, 18)         None
flat            0               18              361
SGF             'aa'            'sa'            ''
KGS             'A19'           'T19'           'pass'
pygtp           (1, 19)         (19, 19)        (0, 0)
"""

import gtp

# We provide more than 19 entries here in case of boards larger than 19 x 19.
_SGF_COLUMNS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
_KGS_COLUMNS = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'


def from_flat(board_size, flat):
  """Converts from a flattened coordinate to a MiniGo coordinate."""
  if flat == board_size * board_size:
    return None
  return divmod(flat, board_size)


def to_flat(board_size, coord):
  """Converts from a MiniGo coordinate to a flattened coordinate."""
  if coord is None:
    return board_size * board_size
  return board_size * coord[0] + coord[1]


def from_sgf(sgfc):
  """Converts from an SGF coordinate to a MiniGo coordinate."""
  if not sgfc:
    return None
  return _SGF_COLUMNS.index(sgfc[1]), _SGF_COLUMNS.index(sgfc[0])


def to_sgf(coord):
  """Converts from a MiniGo coordinate to an SGF coordinate."""
  if coord is None:
    return ''
  return _SGF_COLUMNS[coord[1]] + _SGF_COLUMNS[coord[0]]


def from_kgs(board_size, kgsc):
  """Converts from a KGS coordinate to a MiniGo coordinate."""
  if kgsc == 'pass':
    return None
  kgsc = kgsc.upper()
  col = _KGS_COLUMNS.index(kgsc[0])
  row_from_bottom = int(kgsc[1:])
  return board_size - row_from_bottom, col


def to_kgs(board_size, coord):
  """Converts from a MiniGo coordinate to a KGS coordinate."""
  if coord is None:
    return 'pass'
  y, x = coord
  return '{}{}'.format(_KGS_COLUMNS[x], board_size - y)


def from_pygtp(board_size, pygtpc):
  """Converts from a pygtp coordinate to a MiniGo coordinate."""
  # GTP has a notion of both a Pass and a Resign, both of which are mapped to
  # None, so the conversion is not precisely bijective.
  if pygtpc in (gtp.PASS, gtp.RESIGN):
    return None
  return board_size - pygtpc[1], pygtpc[0] - 1


def to_pygtp(board_size, coord):
  """Converts from a MiniGo coordinate to a pygtp coordinate."""
  if coord is None:
    return gtp.PASS
  return coord[1] + 1, board_size - coord[0]