Minor changes
[matilda.git] / src / scoring.c
blob02ce917d26ab34613837b456845d3b17dc5a507f
1 /*
2 Functions for board scoring that take komi and dynamic komi into consideration.
4 Remember that in Matilda, scores and komi are always doubled to become integer.
5 */
7 #include "config.h"
9 #include <string.h>
10 #include <stdio.h>
12 #include "board.h"
13 #include "cfg_board.h"
14 #include "mcts.h"
15 #include "scoring.h"
16 #include "state_changes.h"
17 #include "timem.h"
18 #include "tactical.h"
19 #include "alloc.h"
22 extern d16 komi;
25 Produces a textual representation of a Go match score., ex: B+3.5, 0
27 void score_to_string(
28 char * dst,
29 d16 score
30 ) {
31 if (score == 0) {
32 snprintf(dst, MAX_PAGE_SIZ, "0");
33 } else if ((score & 1) == 1) {
34 if (score > 0) {
35 snprintf(dst, MAX_PAGE_SIZ, "B+%d.5", score / 2);
36 } else {
37 snprintf(dst, MAX_PAGE_SIZ, "W+%d.5", (-score) / 2);
39 } else {
40 if (score > 0) {
41 snprintf(dst, MAX_PAGE_SIZ, "B+%d", score / 2);
42 } else {
43 snprintf(dst, MAX_PAGE_SIZ, "W+%d", (-score) / 2);
49 Produces a textual representation of a komidashi value.
51 void komi_to_string(
52 char * dst,
53 d16 komi
54 ) {
55 if (komi == 0) {
56 snprintf(dst, MAX_PAGE_SIZ, "0");
57 } else if ((komi & 1) == 1) {
58 if (komi > 0) {
59 snprintf(dst, MAX_PAGE_SIZ, "%d.5", komi / 2);
60 } else {
61 snprintf(dst, MAX_PAGE_SIZ, "-%d.5", (-komi) / 2);
63 } else {
64 if (komi > 0) {
65 snprintf(dst, MAX_PAGE_SIZ, "%d", komi / 2);
66 } else {
67 snprintf(dst, MAX_PAGE_SIZ, "-%d", (-komi) / 2);
73 Scoring by counting stones on the board only.
74 RETURNS positive score for a black win; negative for a white win; 0 for a draw
76 d16 score_stones_only(
77 const u8 p[static TOTAL_BOARD_SIZ]
78 ) {
79 d16 r = 0;
81 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
82 switch (p[m]) {
83 case BLACK_STONE:
84 r += 2;
85 break;
86 case WHITE_STONE:
87 r -= 2;
91 return r - komi;
96 Scoring by counting stones and eyes on the board only.
97 RETURNS positive score for a black win; negative for a white win; 0 for a draw
99 d16 score_stones_and_eyes2(
100 const cfg_board * cb
102 bool _ignored;
103 d16 r = 0;
105 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
106 switch (cb->p[m]) {
107 case BLACK_STONE:
108 r += 2;
109 break;
110 case WHITE_STONE:
111 r -= 2;
112 break;
113 case EMPTY:
114 if (is_4pt_eye(cb, true, m, &_ignored)) {
115 r += 8;
116 ++m;
117 break;
119 if (is_4pt_eye(cb, false, m, &_ignored)) {
120 r -= 8;
121 ++m;
122 break;
124 if (is_2pt_eye(cb, true, m, &_ignored)) {
125 r += 4;
126 break;
128 if (is_2pt_eye(cb, false, m, &_ignored)) {
129 r -= 4;
130 break;
132 if (is_eye(cb, true, m)) {
133 r += 2;
134 break;
136 if (is_eye(cb, false, m)) {
137 r -= 2;
138 break;
143 return r - komi;
147 Scoring by counting stones and eyes on the board only.
148 RETURNS positive score for a black win; negative for a white win; 0 for a draw
150 d16 score_stones_and_eyes(
151 const board * b
153 cfg_board cb;
154 cfg_from_board(&cb, b);
155 d16 ret = score_stones_and_eyes2(&cb);
156 cfg_board_free(&cb);
157 return ret;
161 static void _search(
162 const u8 p[static TOTAL_BOARD_SIZ],
163 move m,
164 bool explored[static TOTAL_BOARD_SIZ],
165 bool * restrict black,
166 bool * restrict white
168 u8 x;
169 u8 y;
170 move_to_coord(m, &x, &y);
172 if (x > 0) {
173 if (p[m + LEFT] == BLACK_STONE) {
174 *black |= true;
175 } else if (p[m + LEFT] == WHITE_STONE) {
176 *white |= true;
177 } else if (explored[m + LEFT] == false) {
178 explored[m + LEFT] = true;
179 _search(p, m + LEFT, explored, black, white);
183 if (x < BOARD_SIZ - 1) {
184 if (p[m + RIGHT] == BLACK_STONE) {
185 *black |= true;
186 } else if (p[m + RIGHT] == WHITE_STONE) {
187 *white |= true;
188 } else if (explored[m + RIGHT] == false) {
189 explored[m + RIGHT] = true;
190 _search(p, m + RIGHT, explored, black, white);
194 if (y > 0) {
195 if (p[m + TOP] == BLACK_STONE) {
196 *black |= true;
197 } else if (p[m + TOP] == WHITE_STONE) {
198 *white |= true;
199 } else if (explored[m + TOP] == false) {
200 explored[m + TOP] = true;
201 _search(p, m + TOP, explored, black, white);
205 if (y < BOARD_SIZ - 1) {
206 if (p[m + BOTTOM] == BLACK_STONE) {
207 *black |= true;
208 } else if (p[m + BOTTOM] == WHITE_STONE) {
209 *white |= true;
210 } else if (explored[m + BOTTOM] == false) {
211 explored[m + BOTTOM] = true;
212 _search(p, m + BOTTOM, explored, black, white);
217 static void _apply(
218 u8 p[static TOTAL_BOARD_SIZ],
219 move m,
220 u8 val
222 u8 x;
223 u8 y;
224 move_to_coord(m, &x, &y);
226 if (x > 0 && p[m + LEFT] == EMPTY) {
227 p[m + LEFT] = val;
228 _apply(p, m + LEFT, val);
231 if (x < BOARD_SIZ - 1 && p[m + RIGHT] == EMPTY) {
232 p[m + RIGHT] = val;
233 _apply(p, m + RIGHT, val);
236 if (y > 0 && p[m + TOP] == EMPTY) {
237 p[m + TOP] = val;
238 _apply(p, m + TOP, val);
241 if (y < BOARD_SIZ - 1 && p[m + BOTTOM] == EMPTY) {
242 p[m + BOTTOM] = val;
243 _apply(p, m + BOTTOM, val);
248 Scoring by counting stones and surrounded area. Also known as area scoring. Does
249 not remove dead stones.
250 RETURNS positive score for a black win; negative for a white win; 0 for a draw
252 d16 score_stones_and_area(
253 const u8 p[static TOTAL_BOARD_SIZ]
255 /* explored intersections array is only used for empty intersections */
256 bool explored[TOTAL_BOARD_SIZ];
257 memset(explored, false, TOTAL_BOARD_SIZ * sizeof(bool));
259 u8 bak[TOTAL_BOARD_SIZ];
260 memcpy(bak, p, TOTAL_BOARD_SIZ);
262 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
263 if (p[m] == EMPTY && !explored[m]) { /* Find owner of empty intersection */
264 bool found_black = false;
265 bool found_white = false;
266 explored[m] = true;
267 _search(p, m, explored, &found_black, &found_white);
269 if (found_black != found_white) { /* established intersection */
270 if (found_black) {
271 bak[m] = BLACK_STONE;
272 _apply(bak, m, BLACK_STONE);
273 } else {
274 bak[m] = WHITE_STONE;
275 _apply(bak, m, WHITE_STONE);
281 d16 r = 0;
282 for (move m = 0; m < TOTAL_BOARD_SIZ; ++m) {
283 if (bak[m] == BLACK_STONE) {
284 r += 2;
285 } else if (bak[m] == WHITE_STONE) {
286 r -= 2;
290 return r - komi;