';
235 | var dialog = $(tpl).appendTo(document.body);
236 |
237 | dialog.find(".weui_actionsheet_menu .weui_actionsheet_cell, .weui_actionsheet_action .weui_actionsheet_cell").each(function(i, e) {
238 | $(e).click(function() {
239 | $.closeActions();
240 | if(actions[i].onClick) {
241 | actions[i].onClick();
242 | }
243 | })
244 | });
245 |
246 | mask.show();
247 | dialog.show();
248 | mask.addClass("weui_mask_visible");
249 | dialog.addClass("weui_actionsheet_toggle");
250 | };
251 |
252 | var hide = function() {
253 | $(".weui_mask").removeClass("weui_mask_visible").transitionEnd(function() {
254 | $(this).remove();
255 | });
256 | $(".weui_actionsheet").removeClass("weui_actionsheet_toggle").transitionEnd(function() {
257 | $(this).remove();
258 | });
259 | }
260 |
261 | $.actions = function(params) {
262 | params = $.extend({}, defaults, params);
263 | show(params);
264 | }
265 |
266 | $.closeActions = function() {
267 | hide();
268 | }
269 |
270 | var defaults = $.actions.prototype.defaults = {
271 | /*actions: [{
272 | text: "菜单",
273 | className: "danger",
274 | onClick: function() {
275 | console.log(1);
276 | }
277 | },{
278 | text: "菜单2",
279 | className: "danger",
280 | onClick: function() {
281 | console.log(2);
282 | }
283 | }]*/
284 | }
285 |
286 | }($);
287 |
288 | /* ===============================================================================
289 | ************ Notification ************
290 | =============================================================================== */
291 | /* global $:true */
292 | +function ($) {
293 | "use strict";
294 |
295 | var distance = 50;
296 | var container, start, diffX, diffY;
297 |
298 | var touchStart = function(e) {
299 | if(container.hasClass("refreshing")) return;
300 | var p = $.getTouchPosition(e);
301 | start = p;
302 | diffX = diffY = 0;
303 | };
304 | var touchMove = function(e) {
305 | if(container.hasClass("refreshing")) return;
306 | if(!start) return false;
307 | if(container.scrollTop() > 0) return;
308 | var p = $.getTouchPosition(e);
309 | diffX = p.x - start.x;
310 | diffY = p.y - start.y;
311 | if(diffY < 0) return;
312 | container.addClass("touching");
313 | e.preventDefault();
314 | e.stopPropagation();
315 | diffY = Math.pow(diffY, 0.8);
316 | container.css("transform", "translate3d(0, "+diffY+"px, 0)");
317 |
318 | if(diffY < distance) {
319 | container.removeClass("pull-up").addClass("pull-down");
320 | } else {
321 | container.removeClass("pull-down").addClass("pull-up");
322 | }
323 | };
324 | var touchEnd = function() {
325 | start = false;
326 | if(diffY <= 0 || container.hasClass("refreshing")) return;
327 | container.removeClass("touching");
328 | container.removeClass("pull-down pull-up");
329 | container.css("transform", "");
330 | if(Math.abs(diffY) <= distance) {
331 | } else {
332 | container.addClass("refreshing");
333 | container.trigger("pull-to-refresh");
334 | }
335 |
336 |
337 | };
338 |
339 | var attachEvents = function(el) {
340 | el = $(el);
341 | el.addClass("weui-pull-to-refresh");
342 | container = el;
343 | el.on($.touchEvents.start, touchStart);
344 | el.on($.touchEvents.move, touchMove);
345 | el.on($.touchEvents.end, touchEnd);
346 | };
347 |
348 | var pullToRefresh = function(el) {
349 | attachEvents(el);
350 | };
351 |
352 | var pullToRefreshDone = function(el) {
353 | $(el).removeClass("refreshing");
354 | }
355 |
356 | $.fn.pullToRefresh = function() {
357 | return this.each(function() {
358 | pullToRefresh(this);
359 | });
360 | }
361 |
362 | $.fn.pullToRefreshDone = function() {
363 | return this.each(function() {
364 | pullToRefreshDone(this);
365 | });
366 | }
367 |
368 | }($);
369 |
370 | /* ===============================================================================
371 | ************ Notification ************
372 | =============================================================================== */
373 | /* global $:true */
374 | +function ($) {
375 | "use strict";
376 |
377 | var distance = 50;
378 | var container;
379 |
380 | var scroll = function() {
381 | var offset = container.scrollHeight() - ($(window).height() + container.scrollTop());
382 | if(offset <= distance) {
383 | container.trigger("infinite");
384 | }
385 | }
386 |
387 | var attachEvents = function(el, off) {
388 | el = $(el);
389 | container = el;
390 | var scrollContainer = (el[0].tagName.toUpperCase() === "BODY" ? $(document) : el);
391 | scrollContainer[off ? "off" : "on"]("scroll", scroll);
392 | };
393 |
394 | var infinite = function(el) {
395 | attachEvents(el);
396 | }
397 |
398 | var infinite = function(el) {
399 | attachEvents(el);
400 | }
401 |
402 | $.fn.infinite = function() {
403 | return this.each(function() {
404 | infinite(this);
405 | });
406 | }
407 | $.fn.destroyInfinite = function() {
408 | return this.each(function() {
409 | attachEvents(this, true);
410 | });
411 | }
412 |
413 | }($);
414 |
--------------------------------------------------------------------------------
/genetic_algo/genetic_AI.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import numpy as np
3 | from random import choice
4 |
5 | def strategy(state, score):
6 | # score is a global variable
7 | # score is a 28-dimension array
8 | # initial: [1000000, 20000, 6100, 6000, 1100, 1000, 300, 290, 290, 290, 100, 10, 3, 1,
9 | # 1000000, 100000, 65000, 65000, 5500, 5000, 200, 200, 200, 200, 90, 9, 4, 1]
10 | """ Information provided to you:
11 | state = (board, last_move, playing, board_size)
12 | board = (x_stones, o_stones)
13 | stones is a set contains positions of one player's stones. e.g.
14 | x_stones = {(8,8), (8,9), (8,10), (8,11)}
15 | playing = 0|1, the current player's index
16 |
17 | Your strategy will return a position code for the next stone, e.g. (8,7)
18 | """
19 | board, last_move, playing, board_size = state
20 | row = board_size
21 | col = board_size
22 | # create a table to record the board state
23 | # 1: occupied by self
24 | # 2: occupied by opponent
25 | # 0: available
26 | table = np.zeros([row, col])
27 | for i in range(row):
28 | for j in range(col):
29 | if playing == 1:
30 | if (i+1, j+1) in board[0]:
31 | table[i, j] = 2
32 | elif (i+1, j+1) in board[1]:
33 | table[i, j] = 1
34 | else:
35 | if (i+1, j+1) in board[0]:
36 | table[i, j] = 1
37 | elif (i+1, j+1) in board[1]:
38 | table[i, j] = 2
39 | # 获取该点4个方向的棋型
40 | def getstring(point):
41 | x = point[0]
42 | y = point[1]
43 | # vertical
44 | getLine1 = ''
45 | for k in range(max(x - 4, 0), min(x + 5, 15)):
46 | getLine1 += str(int(table[k, y]))
47 | if x-4<0: getLine1 = '*'+getLine1
48 | if x+4>14: getLine1 = getLine1+'*'
49 | # horizonal
50 | getLine2 = ''
51 | for k in range(max(y - 4, 0), min(y + 5, 15)):
52 | getLine2 += str(int(table[x, k]))
53 | if y-4<0: getLine2 = '*'+getLine2
54 | if y+4>14: getLine2 = getLine2+'*'
55 | # Oblique 45
56 | getLine3 = ''
57 | bx = max(0, x - 4)
58 | by = max(0, y - 4)
59 | ux = min(14, x + 4)
60 | uy = min(14, y + 4)
61 | for k in range(max(bx - x, by - y), min(ux - x, uy - y)+1):
62 | getLine3 += str(int(table[x + k, y + k]))
63 | if x-4<0 or y-4<0: getLine3 = '*'+getLine3
64 | if x+4>14 or y+4>14: getLine3 = getLine3+'*'
65 | # Oblique 135
66 | getLine4 = ''
67 | for k in range(max(bx - x, y - uy), min(ux - x, y - by)+1):
68 | getLine4 += str(int(table[x + k, y - k]))
69 | if x-4<0 or y+4>14: getLine4 = '*'+getLine4
70 | if x+4>14 or y-4<0: getLine4 = getLine4+'*'
71 |
72 | return [getLine1, getLine2, getLine3, getLine4]
73 |
74 | # 判断我方棋型
75 | def judgeType1(getline):
76 | if '11111' in getline:
77 | return 'win5'
78 | if '011110' in getline:
79 | return 'alive4'
80 | if '211110' in getline or '011112' in getline\
81 | or '*11110' in getline or '01111*' in getline:
82 | return 'lian-rush4'
83 | if '11101' in getline or '10111' in getline\
84 | or '11011' in getline:
85 | return 'tiao-rush4'
86 | if '001110' in getline or '011100' in getline:
87 | return 'lian-alive3'
88 | if '011010' in getline or '010110' in getline:
89 | return 'tiao-alive3'
90 | if '211100' in getline or '001112' in getline\
91 | or '*11100' in getline or '00111*' in getline:
92 | return 'lian-sleep3'
93 | if '211010' in getline or '010112' in getline\
94 | or '*11010' in getline or '01011*' in getline\
95 | or '210110' in getline or '011012' in getline\
96 | or '*10110' in getline or '01101*' in getline:
97 | return 'tiao-sleep3'
98 | if '11001' in getline or '10011' in getline\
99 | or '10101' in getline:
100 | return 'te-sleep3'
101 | if '2011102' in getline or '*011102' in getline\
102 | or '201110*' in getline or '*01110*' in getline:
103 | return 'jia-alive3'
104 | if '001100' in getline or '011000' in getline\
105 | or '000110' in getline or '001010' in getline\
106 | or '010100' in getline or '010010' in getline:
107 | return 'alive2'
108 | if '211000' in getline or '000112' in getline\
109 | or '*11000' in getline or '00011*' in getline\
110 | or '210100' in getline or '001012' in getline\
111 | or '*10100' in getline or '00101*' in getline\
112 | or '210010' in getline or '010012' in getline\
113 | or '*10010' in getline or '01001*' in getline\
114 | or '10001' in getline or '2010102' in getline\
115 | or '*01010*' in getline or '201010*' in getline\
116 | or '*010102' in getline or '2011002' in getline\
117 | or '2001102' in getline or '*011002' in getline\
118 | or '200110*' in getline or '201100*' in getline\
119 | or '*001102' in getline:
120 | return 'sleep2'
121 | if '010' in getline:
122 | return 'alive1'
123 | else:
124 | return 'nothreat'
125 |
126 | # 判断对方棋型
127 | def judgeType2(getline):
128 | if '22222' in getline:
129 | return 'win5'
130 | if '022220' in getline:
131 | return 'alive4'
132 | if '122220' in getline or '022221' in getline\
133 | or '*22220' in getline or '02222*' in getline:
134 | return 'lian-rush4'
135 | if '22202' in getline or '20222' in getline\
136 | or '22022' in getline:
137 | return 'tiao-rush4'
138 | if '002220' in getline or '022200' in getline:
139 | return 'lian-alive3'
140 | if '022020' in getline or '020220' in getline:
141 | return 'tiao-alive3'
142 | if '122200' in getline or '002221' in getline\
143 | or '*22200' in getline or '00222*' in getline:
144 | return 'lian-sleep3'
145 | if '122020' in getline or '020221' in getline\
146 | or '*22020' in getline or '02022*' in getline\
147 | or '120220' in getline or '022021' in getline\
148 | or '*20220' in getline or '02202*' in getline:
149 | return 'tiao-sleep3'
150 | if '22002' in getline or '20022' in getline\
151 | or '20202' in getline:
152 | return 'te-sleep3'
153 | if '1022201' in getline or '*022201' in getline\
154 | or '102220*' in getline or '*02220*' in getline:
155 | return 'jia-alive3'
156 | if '002200' in getline or '022000' in getline\
157 | or '000220' in getline or '002020' in getline\
158 | or '020200' in getline or '020020' in getline:
159 | return 'alive2'
160 | if '122000' in getline or '000221' in getline\
161 | or '*22000' in getline or '00022*' in getline\
162 | or '120200' in getline or '002021' in getline\
163 | or '*20200' in getline or '00202*' in getline\
164 | or '120020' in getline or '020021' in getline\
165 | or '*20020' in getline or '02002*' in getline\
166 | or '20002' in getline or '1020201' in getline\
167 | or '*02020*' in getline or '102020*' in getline\
168 | or '*020201' in getline or '1022001' in getline\
169 | or '1002201' in getline or '*022001' in getline\
170 | or '100220*' in getline or '102200*' in getline\
171 | or '*002201' in getline:
172 | return 'sleep2'
173 | if '020' in getline:
174 | return 'alive1'
175 | else:
176 | return 'nothreat'
177 |
178 | # 计算我方形势分数
179 | def evaluate_self(table):
180 | row, col = table.shape
181 | myscore = 0
182 | for i in range(row):
183 | for j in range(col):
184 | if table[i, j] == 1:
185 | point = (i, j)
186 | myType={'win5':0, 'alive4':0, 'lian-rush4':0, 'tiao-rush4':0, 'lian-alive3':0, 'tiao-alive3':0,\
187 | 'lian-sleep3':0, 'tiao-sleep3':0, 'te-sleep3':0, 'jia-alive3':0,\
188 | 'alive2':0, 'sleep2':0, 'alive1':0, 'nothreat':0}
189 | lines = getstring(point)
190 | for item0 in lines:
191 | tmp1 = judgeType1(item0)
192 | myType[tmp1] += 1
193 | # my score
194 | myscore += score[0]*myType['win5']+score[1]*myType['alive4']+ \
195 | score[2]*myType['lian-rush4']+score[3]*myType['tiao-rush4']+ \
196 | score[4]*myType['lian-alive3']+score[5]*myType['tiao-alive3']+ \
197 | score[6]*myType['lian-sleep3']+score[7]*myType['tiao-sleep3']+\
198 | score[8]*myType['te-sleep3']+score[9]*myType['jia-alive3']+\
199 | score[10]*myType['alive2']+score[11]*myType['sleep2']+\
200 | score[12]*myType['alive1']+score[13]*myType['nothreat']
201 | return myscore
202 |
203 | # 计算敌方的形势分数
204 | def evaluate_op(table):
205 | row, col = table.shape
206 | opscore = 0
207 | for i in range(row):
208 | for j in range(col):
209 | if table[i, j] == 2:
210 | point = (i, j)
211 | opType = {'win5':0, 'alive4':0, 'lian-rush4':0, 'tiao-rush4':0, 'lian-alive3':0, 'tiao-alive3':0,\
212 | 'lian-sleep3':0, 'tiao-sleep3':0, 'te-sleep3':0, 'jia-alive3':0,\
213 | 'alive2':0, 'sleep2':0, 'alive1':0, 'nothreat':0}
214 | lines = getstring(point)
215 | for item0 in lines:
216 | tmp2 = judgeType2(item0)
217 | opType[tmp2] += 1
218 | # opponent score
219 | opscore += score[14]*opType['win5']+score[15]*opType['alive4']+ \
220 | score[16]*opType['lian-rush4']+score[17]*opType['tiao-rush4']+ \
221 | score[18]*opType['lian-alive3']+score[19]*opType['tiao-alive3']+ \
222 | score[20]*opType['lian-sleep3']+score[21]*opType['tiao-sleep3']+\
223 | score[22]*opType['te-sleep3']+score[23]*opType['jia-alive3']+\
224 | score[24]*opType['alive2']+score[25]*opType['sleep2']+\
225 | score[26]*opType['alive1']+score[27]*opType['nothreat']
226 | return opscore
227 |
228 |
229 | #随机返回一个score最大的位置
230 | def randomChoose(scoretable):
231 | maxValue = max(scoretable.items(), key=lambda x: x[1])[1]
232 | positions=[]
233 | for item in scoretable.items():
234 | if item[1]==maxValue:
235 | positions.append(item[0])
236 | return choice(positions)
237 |
238 |
239 | if len(board[0]) == 0 and len(board[1]) == 0:
240 | return (board_size/2 + 1, board_size/2 + 1)
241 | else:
242 | # 获得局部搜索区域的下标
243 | sumBoard = board[0] | board[1]
244 | xmax = min(max(sumBoard, key=lambda x: x[0])[0] + 2, 15)
245 | ymax = min(max(sumBoard, key=lambda x: x[1])[1] + 2, 15)
246 | xmin = max(min(sumBoard, key=lambda x: x[0])[0] - 3, 0)
247 | ymin = max(min(sumBoard, key=lambda x: x[1])[1] - 3, 0)
248 |
249 | scoretable={}
250 | for i in range(xmin, xmax):
251 | for j in range(ymin, ymax):
252 | if table[i, j] == 0:
253 | #old = evaluate_self(table)-defend*evaluate_op(table)
254 | table[i, j] = 1
255 | scoretable[(i, j)] = evaluate_self(table)-evaluate_op(table)
256 | table[i, j] = 0
257 | self_position = randomChoose(scoretable)
258 | return (self_position[0]+1, self_position[1]+1)
259 |
260 |
--------------------------------------------------------------------------------
/app/gomoku.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 | # -- coding: utf-8 --
3 | from __future__ import print_function, division
4 | import os, sys, time, collections
5 | from functools import update_wrapper
6 | import pickle
7 |
8 | def decorator(d):
9 | "Make function d a decorator: d wraps a function fn."
10 | def _d(fn):
11 | return update_wrapper(d(fn), fn)
12 | update_wrapper(_d, d)
13 | return _d
14 |
15 | @decorator
16 | def memo(f):
17 | """Decorator that caches the return value for each call to f(args).
18 | Then when called again with same args, we can just look it up."""
19 | cache = {}
20 | def _f(*args):
21 | try:
22 | return cache[args]
23 | except KeyError:
24 | cache[args] = result = f(*args)
25 | return result
26 | except TypeError:
27 | # some element of args refuses to be a dict key
28 | return f(args)
29 | _f.cache = cache
30 | return _f
31 |
32 | @memo
33 | def colored(s, color=''):
34 | if color.lower() == 'green':
35 | return '\033[92m' + s + '\033[0m'
36 | elif color.lower() == 'yellow':
37 | return '\033[93m' + s + '\033[0m'
38 | elif color.lower() == 'red':
39 | return '\033[91m' + s + '\033[0m'
40 | elif color.lower() == 'blue':
41 | return '\033[94m' + s + '\033[0m'
42 | elif color.lower() == 'bold':
43 | return '\033[1m' + s + '\033[0m'
44 | else:
45 | return s
46 |
47 | class Gomoku(object):
48 | """ Gomoku Game Rules:
49 | Two players alternatively put their stone on the board. First one got five in a row wins.
50 | """
51 |
52 | def __init__(self, board_size=15, players=None, fastmode=False, first_center=None, silent_mode=False, winning_num=5):
53 | self.reset()
54 | self.board_size = board_size
55 | self.fastmode = fastmode
56 | self.playing = None
57 | self.players = [Player(player_name) for player_name in players]
58 | self.winning_stones = set()
59 | self.last_move = None
60 | self.first_center = first_center
61 | self.silent_mode = silent_mode
62 | self.winning_num = winning_num
63 |
64 | @property
65 | def state(self):
66 | return (self.board, self.last_move, self.playing, self.board_size)
67 |
68 | def load_state(self, state):
69 | (self.board, self.last_move, self.playing, self.board_size) = state
70 |
71 | def reset(self):
72 | self.board = (set(), set())
73 |
74 | def print_board(self):
75 | print(' '*4 + ' '.join([chr(97+i) for i in range(self.board_size)]))
76 | print(' '*3 + '='*(2*self.board_size))
77 | for x in range(1, self.board_size+1):
78 | row = ['%2s|'%x]
79 | for y in range(1, self.board_size+1):
80 | if (x,y) in self.board[0]:
81 | c = 'x'
82 | elif (x,y) in self.board[1]:
83 | c = 'o'
84 | else:
85 | c = '-'
86 | if (x,y) in self.winning_stones or (x,y) == self.last_move:
87 | c = colored(c, 'green')
88 | row.append(c)
89 | print(' '.join(row))
90 |
91 | def play(self):
92 | if self.fastmode < 2: print("Game Start!")
93 | i_turn = len(self.board[0]) + len(self.board[1])
94 | new_step = None
95 | while True:
96 | if self.fastmode < 2: print("----- Turn %d -------" % i_turn)
97 | self.playing = i_turn % 2
98 | if self.fastmode < 2 and not self.silent_mode:
99 | self.print_board()
100 | current_player = self.players[self.playing]
101 | other_player = self.players[int(not self.playing)]
102 | if self.fastmode < 2: print("--- %s's turn ---" % current_player.name)
103 | max_try = 5
104 | for i_try in range(max_try):
105 | action = current_player.strategy(self.state)
106 | if action == (0, 0):
107 | print("Player %s admit defeat!" % current_player.name)
108 | winner = other_player.name
109 | if self.fastmode < 2: print("Winner is %s"%winner)
110 | return winner
111 | self.last_move = action
112 | if self.place_stone() is True:
113 | break
114 | if i_try == max_try-1:
115 | print("Player %s has made %d illegal moves, he lost."%(current_player.name, max_try))
116 | winner = other_player.name
117 | print("Winner is %s"%winner)
118 | return winner
119 | # check if current player wins
120 | winner = self.check_winner()
121 | if winner:
122 | if not self.silent_mode:
123 | self.print_board()
124 | print("########## %s is the WINNER! #########" % current_player.name)
125 | return winner
126 | elif i_turn == self.board_size ** 2 - 1:
127 | print("This game is a tie!")
128 | return "Tie"
129 | i_turn += 1
130 |
131 | def place_stone(self):
132 | # check if this position is on the board
133 | r, c = self.last_move
134 | if r < 1 or r > self.board_size or c < 1 or c > self.board_size:
135 | print("This position is outside the board!")
136 | return False
137 | # check if this position is already taken
138 | taken_pos = self.board[0] | self.board[1]
139 | if self.first_center is True and len(taken_pos) == 0:
140 | # if this is the very first move, it must be on the center
141 | center = int((self.board_size+1)/2)
142 | if r != center or c != center:
143 | print("This is the first move, please put it on the center (%s%s)!"% (str(center),chr(center+96)))
144 | return False
145 | elif self.last_move in taken_pos:
146 | print("This position is already taken!")
147 | return False
148 | self.board[self.playing].add(self.last_move)
149 | return True
150 |
151 | def check_winner(self):
152 | r, c = self.last_move
153 | my_stones = self.board[self.playing]
154 | # find any nearby stone
155 | nearby_stones = set()
156 | for x in range(max(r-1, 1), min(r+2, self.board_size+1)):
157 | for y in range(max(c-1, 1), min(c+2, self.board_size+1)):
158 | stone = (x,y)
159 | if stone in my_stones and (2*r-x, 2*c-y) not in nearby_stones:
160 | nearby_stones.add(stone)
161 | for nearby_s in nearby_stones:
162 | winning_stones = {self.last_move, nearby_s}
163 | nr, nc = nearby_s
164 | dx, dy = nr-r, nc-c
165 | # try to extend in this direction
166 | for i in range(1,4):
167 | ext_stone = (nr+dx*i, nc+dy*i)
168 | if ext_stone in my_stones:
169 | winning_stones.add(ext_stone)
170 | else:
171 | break
172 | # try to extend in the opposite direction
173 | for i in range(1,5):
174 | ext_stone = (r-dx*i, c-dy*i)
175 | if ext_stone in my_stones:
176 | winning_stones.add(ext_stone)
177 | else:
178 | break
179 | if len(winning_stones) >= self.winning_num:
180 | self.winning_stones = winning_stones
181 | return self.players[self.playing].name
182 | return None
183 |
184 | def delay(self, n):
185 | """ Delay n seconds if not in fastmode"""
186 | if not self.fastmode:
187 | time.sleep(n)
188 |
189 | def get_strategy(self, p):
190 | return p.strategy(self.state)
191 |
192 | class Player(object):
193 | @property
194 | def name(self):
195 | return self._name
196 |
197 | @name.setter
198 | def name(self, value):
199 | try:
200 | self._name = str(value)
201 | except:
202 | raise TypeError("Player Name must be a string.")
203 |
204 | def __repr__(self):
205 | return "Player %s"%self.name
206 |
207 | def __init__(self, name):
208 | self.name = name
209 | # Allow name to be appended by a number
210 | if (not name[0].isdigit()) and (name[-1].isdigit()):
211 | name = name[:-1]
212 | # search for the strategy file
213 | for f in os.listdir('.'):
214 | filename, fileext = os.path.splitext(f)
215 | if name.lower() == filename.lower() and fileext == '.py':
216 | print('strategy() found in %s, will use as AI.'%f)
217 | p = __import__(filename)
218 | try:
219 | self.strategy = p.strategy
220 | except:
221 | raise RuntimeError("Function strategy(state) is not found in %s"%filename)
222 | try:
223 | self.finish = p.finish
224 | except:
225 | pass
226 | # if not found, use manual input
227 | if not hasattr(self, 'strategy'):
228 | self.strategy = self.human_input
229 |
230 | def human_input(self, state):
231 | """ Ask player to place stone """
232 | r, c = 0, 0
233 | for t in range(3):
234 | try:
235 | s = raw_input('Please place stone, enter code like "8h": ')
236 | if s == 'save':
237 | pickle.dump(state, open('saved.state','wb'))
238 | print("Current game state saved to saved.state!")
239 | continue
240 | if any(phrase in s for phrase in ['giveup','throw','admit']):
241 | break
242 | r, c = s[:-1], s[-1]
243 | r = int(r)
244 | c = ord(c) - 96
245 | break
246 | except:
247 | print("Invalid input! Please try again. (%d)"%(3-t))
248 | pass
249 | return (r,c)
250 |
251 |
252 | def main():
253 | import argparse
254 | parser = argparse.ArgumentParser("Play the Gomoku Game!", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
255 | parser.add_argument('players', nargs='*', default=['AI', 'Player1'], help='Names of Players, the first one plays first.')
256 | parser.add_argument('--board_size', type=int, default=15, help='Size of the board.')
257 | parser.add_argument('--first_center', action='store_true', help='The first move must be on the center of board?')
258 | parser.add_argument('--fast', action='store_true', help='Run the game in fast mode.')
259 | parser.add_argument('-n', '--ngames', type=int, help='Play a number of games to gather statistics.')
260 | parser.add_argument('--fixorder', action='store_true', help='Fix the order of players in a multi-game series.')
261 | parser.add_argument('--load', help='Load a previously saved state to continue the game.')
262 | args = parser.parse_args()
263 |
264 | # fix the .py after player names
265 | players = []
266 | for p in args.players:
267 | players.append(p[:-3] if p.endswith('.py') else p)
268 | assert len(players) == 2, "Gomoku can only be played with 2 players."
269 |
270 |
271 | game = Gomoku(board_size=args.board_size, players=players, fastmode=args.fast, first_center=args.first_center)
272 | if args.load:
273 | state = pickle.load(open(args.load,'rb'))
274 | game.load_state(state)
275 |
276 | if args.ngames is None:
277 | game.play()
278 | else:
279 | # check if all players have stategy function setup
280 | for p in game.players:
281 | if p.strategy.__name__ == 'human_input':
282 | print("%s need a strategy function to enter the auto-play mode. Exiting.."%p.name)
283 | return
284 | print("Gathering result of %d games..."%args.ngames)
285 | game.fastmode = 2
286 | game_output = open('game_results.txt','w')
287 | winner_board = collections.OrderedDict([(p.name, 0) for p in game.players])
288 | winner_board["Tie"] = 0
289 | def playone(i):
290 | game_output.write('Game %-4d .'%(i+1))
291 | game.reset()
292 | winner = game.play()
293 | winner_board[winner] += 1
294 | game_output.write('Game %-4d: Winner is %s\n'%(i+1, winner))
295 | game_output.flush()
296 | for i in range(args.ngames):
297 | playone(i)
298 | # switch the order of the players
299 | game.players = game.players[1:] + [game.players[0]]
300 | game_output.close()
301 | print("Name | Games Won")
302 | for name, nwin in winner_board.items():
303 | print("%-7s | %7d"%(name, nwin))
304 | # Let the players finish their game
305 | for p in game.players:
306 | if hasattr(p, 'finish'):
307 | p.finish()
308 |
309 | if __name__ == "__main__":
310 | main()
311 |
--------------------------------------------------------------------------------
/genetic_algo/gomoku.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 | # -- coding: utf-8 --
3 | from __future__ import print_function, division
4 | import os, sys, time, collections
5 | from functools import update_wrapper
6 | import pickle
7 |
8 | def decorator(d):
9 | "Make function d a decorator: d wraps a function fn."
10 | def _d(fn):
11 | return update_wrapper(d(fn), fn)
12 | update_wrapper(_d, d)
13 | return _d
14 |
15 | @decorator
16 | def memo(f):
17 | """Decorator that caches the return value for each call to f(args).
18 | Then when called again with same args, we can just look it up."""
19 | cache = {}
20 | def _f(*args):
21 | try:
22 | return cache[args]
23 | except KeyError:
24 | cache[args] = result = f(*args)
25 | return result
26 | except TypeError:
27 | # some element of args refuses to be a dict key
28 | return f(args)
29 | _f.cache = cache
30 | return _f
31 |
32 | @memo
33 | def colored(s, color=''):
34 | if color.lower() == 'green':
35 | return '\033[92m' + s + '\033[0m'
36 | elif color.lower() == 'yellow':
37 | return '\033[93m' + s + '\033[0m'
38 | elif color.lower() == 'red':
39 | return '\033[91m' + s + '\033[0m'
40 | elif color.lower() == 'blue':
41 | return '\033[94m' + s + '\033[0m'
42 | elif color.lower() == 'bold':
43 | return '\033[1m' + s + '\033[0m'
44 | else:
45 | return s
46 |
47 | class Gomoku(object):
48 | """ Gomoku Game Rules:
49 | Two players alternatively put their stone on the board. First one got five in a row wins.
50 | """
51 |
52 | def __init__(self, board_size=15, players=None, fastmode=False, first_center=None, silent_mode=False, winning_num=5):
53 | self.reset()
54 | self.board_size = board_size
55 | self.fastmode = fastmode
56 | self.playing = None
57 | self.players = [Player(player_name) for player_name in players]
58 | self.winning_stones = set()
59 | self.last_move = None
60 | self.first_center = first_center
61 | self.silent_mode = silent_mode
62 | self.winning_num = winning_num
63 |
64 | @property
65 | def state(self):
66 | return (self.board, self.last_move, self.playing, self.board_size)
67 |
68 | def load_state(self, state):
69 | (self.board, self.last_move, self.playing, self.board_size) = state
70 |
71 | def reset(self):
72 | self.board = (set(), set())
73 |
74 | def print_board(self):
75 | print(' '*4 + ' '.join([chr(97+i) for i in range(self.board_size)]))
76 | print(' '*3 + '='*(2*self.board_size))
77 | for x in range(1, self.board_size+1):
78 | row = ['%2s|'%x]
79 | for y in range(1, self.board_size+1):
80 | if (x,y) in self.board[0]:
81 | c = 'x'
82 | elif (x,y) in self.board[1]:
83 | c = 'o'
84 | else:
85 | c = '-'
86 | if (x,y) in self.winning_stones or (x,y) == self.last_move:
87 | c = colored(c, 'green')
88 | row.append(c)
89 | print(' '.join(row))
90 |
91 | def play(self):
92 | if self.fastmode < 2: print("Game Start!")
93 | i_turn = len(self.board[0]) + len(self.board[1])
94 | new_step = None
95 | while True:
96 | if self.fastmode < 2: print("----- Turn %d -------" % i_turn)
97 | self.playing = i_turn % 2
98 | if self.fastmode < 2 and not self.silent_mode:
99 | self.print_board()
100 | current_player = self.players[self.playing]
101 | other_player = self.players[int(not self.playing)]
102 | if self.fastmode < 2: print("--- %s's turn ---" % current_player.name)
103 | max_try = 5
104 | for i_try in range(max_try):
105 | action = current_player.strategy(self.state)
106 | if action == (0, 0):
107 | print("Player %s admit defeat!" % current_player.name)
108 | winner = other_player.name
109 | if self.fastmode < 2: print("Winner is %s"%winner)
110 | return winner
111 | self.last_move = action
112 | if self.place_stone() is True:
113 | break
114 | if i_try == max_try-1:
115 | print("Player %s has made %d illegal moves, he lost."%(current_player.name, max_try))
116 | winner = other_player.name
117 | print("Winner is %s"%winner)
118 | return winner
119 | # check if current player wins
120 | winner = self.check_winner()
121 | if winner:
122 | if not self.silent_mode:
123 | self.print_board()
124 | print("########## %s is the WINNER! #########" % current_player.name)
125 | return winner
126 | elif i_turn == self.board_size ** 2 - 1:
127 | print("This game is a tie!")
128 | return "Tie"
129 | i_turn += 1
130 |
131 | def place_stone(self):
132 | # check if this position is on the board
133 | r, c = self.last_move
134 | if r < 1 or r > self.board_size or c < 1 or c > self.board_size:
135 | print("This position is outside the board!")
136 | return False
137 | # check if this position is already taken
138 | taken_pos = self.board[0] | self.board[1]
139 | if self.first_center is True and len(taken_pos) == 0:
140 | # if this is the very first move, it must be on the center
141 | center = int((self.board_size+1)/2)
142 | if r != center or c != center:
143 | print("This is the first move, please put it on the center (%s%s)!"% (str(center),chr(center+96)))
144 | return False
145 | elif self.last_move in taken_pos:
146 | print("This position is already taken!")
147 | return False
148 | self.board[self.playing].add(self.last_move)
149 | return True
150 |
151 | def check_winner(self):
152 | r, c = self.last_move
153 | my_stones = self.board[self.playing]
154 | # find any nearby stone
155 | nearby_stones = set()
156 | for x in range(max(r-1, 1), min(r+2, self.board_size+1)):
157 | for y in range(max(c-1, 1), min(c+2, self.board_size+1)):
158 | stone = (x,y)
159 | if stone in my_stones and (2*r-x, 2*c-y) not in nearby_stones:
160 | nearby_stones.add(stone)
161 | for nearby_s in nearby_stones:
162 | winning_stones = {self.last_move, nearby_s}
163 | nr, nc = nearby_s
164 | dx, dy = nr-r, nc-c
165 | # try to extend in this direction
166 | for i in range(1,4):
167 | ext_stone = (nr+dx*i, nc+dy*i)
168 | if ext_stone in my_stones:
169 | winning_stones.add(ext_stone)
170 | else:
171 | break
172 | # try to extend in the opposite direction
173 | for i in range(1,5):
174 | ext_stone = (r-dx*i, c-dy*i)
175 | if ext_stone in my_stones:
176 | winning_stones.add(ext_stone)
177 | else:
178 | break
179 | if len(winning_stones) >= self.winning_num:
180 | self.winning_stones = winning_stones
181 | return self.players[self.playing].name
182 | return None
183 |
184 | def delay(self, n):
185 | """ Delay n seconds if not in fastmode"""
186 | if not self.fastmode:
187 | time.sleep(n)
188 |
189 | def get_strategy(self, p):
190 | return p.strategy(self.state)
191 |
192 | class Player(object):
193 | @property
194 | def name(self):
195 | return self._name
196 |
197 | @name.setter
198 | def name(self, value):
199 | try:
200 | self._name = str(value)
201 | except:
202 | raise TypeError("Player Name must be a string.")
203 |
204 | def __repr__(self):
205 | return "Player %s"%self.name
206 |
207 | def __init__(self, name):
208 | self.name = name
209 | # Allow name to be appended by a number
210 | if (not name[0].isdigit()) and (name[-1].isdigit()):
211 | name = name[:-1]
212 | # search for the strategy file
213 | for f in os.listdir('.'):
214 | filename, fileext = os.path.splitext(f)
215 | if name.lower() == filename.lower() and fileext == '.py':
216 | print('strategy() found in %s, will use as AI.'%f)
217 | p = __import__(filename)
218 | try:
219 | self.strategy = p.strategy
220 | except:
221 | raise RuntimeError("Function strategy(state) is not found in %s"%filename)
222 | try:
223 | self.finish = p.finish
224 | except:
225 | pass
226 | # if not found, use manual input
227 | if not hasattr(self, 'strategy'):
228 | self.strategy = self.human_input
229 |
230 | def human_input(self, state):
231 | """ Ask player to place stone """
232 | r, c = 0, 0
233 | for t in range(3):
234 | try:
235 | s = raw_input('Please place stone, enter code like "8h": ')
236 | if s == 'save':
237 | pickle.dump(state, open('saved.state','wb'))
238 | print("Current game state saved to saved.state!")
239 | continue
240 | if any(phrase in s for phrase in ['giveup','throw','admit']):
241 | break
242 | r, c = s[:-1], s[-1]
243 | r = int(r)
244 | c = ord(c) - 96
245 | break
246 | except:
247 | print("Invalid input! Please try again. (%d)"%(3-t))
248 | pass
249 | return (r,c)
250 |
251 |
252 | def main():
253 | import argparse
254 | parser = argparse.ArgumentParser("Play the Gomoku Game!", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
255 | parser.add_argument('players', nargs='*', default=['AI', 'Player1'], help='Names of Players, the first one plays first.')
256 | parser.add_argument('--board_size', type=int, default=15, help='Size of the board.')
257 | parser.add_argument('--first_center', action='store_true', help='The first move must be on the center of board?')
258 | parser.add_argument('--fast', action='store_true', help='Run the game in fast mode.')
259 | parser.add_argument('-n', '--ngames', type=int, help='Play a number of games to gather statistics.')
260 | parser.add_argument('--fixorder', action='store_true', help='Fix the order of players in a multi-game series.')
261 | parser.add_argument('--load', help='Load a previously saved state to continue the game.')
262 | args = parser.parse_args()
263 |
264 | # fix the .py after player names
265 | players = []
266 | for p in args.players:
267 | players.append(p[:-3] if p.endswith('.py') else p)
268 | assert len(players) == 2, "Gomoku can only be played with 2 players."
269 |
270 |
271 | game = Gomoku(board_size=args.board_size, players=players, fastmode=args.fast, first_center=args.first_center)
272 | if args.load:
273 | state = pickle.load(open(args.load,'rb'))
274 | game.load_state(state)
275 |
276 | if args.ngames is None:
277 | game.play()
278 | else:
279 | # check if all players have stategy function setup
280 | for p in game.players:
281 | if p.strategy.__name__ == 'human_input':
282 | print("%s need a strategy function to enter the auto-play mode. Exiting.."%p.name)
283 | return
284 | print("Gathering result of %d games..."%args.ngames)
285 | game.fastmode = 2
286 | game_output = open('game_results.txt','w')
287 | winner_board = collections.OrderedDict([(p.name, 0) for p in game.players])
288 | winner_board["Tie"] = 0
289 | def playone(i):
290 | game_output.write('Game %-4d .'%(i+1))
291 | game.reset()
292 | winner = game.play()
293 | winner_board[winner] += 1
294 | game_output.write('Game %-4d: Winner is %s\n'%(i+1, winner))
295 | game_output.flush()
296 | for i in range(args.ngames):
297 | playone(i)
298 | # switch the order of the players
299 | game.players = game.players[1:] + [game.players[0]]
300 | game_output.close()
301 | print("Name | Games Won")
302 | for name, nwin in winner_board.items():
303 | print("%-7s | %7d"%(name, nwin))
304 | # Let the players finish their game
305 | for p in game.players:
306 | if hasattr(p, 'finish'):
307 | p.finish()
308 |
309 | if __name__ == "__main__":
310 | main()
311 |
--------------------------------------------------------------------------------
/AI3.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | import copy
4 | import time
5 | from random import choice, shuffle
6 | from math import log, sqrt
7 |
8 | def strategy(state):
9 | """ Information provided to you:
10 | state = (board, last_move, playing, board_size)
11 | board = (x_stones, o_stones)
12 | stones is a set contains positions of one player's stones. e.g.
13 | x_stones = {(8,8), (8,9), (8,10), (8,11)}
14 | playing = 0|1, the current player's index
15 |
16 | Your strategy will return a position code for the next stone, e.g. (8,7)
17 | """
18 | board, last_move, playing, board_size = state
19 | x_stones, o_stones = board
20 | if last_move == None:
21 | return (board_size/2 + 1, board_size/2 + 1)
22 |
23 | board_init = Board(width=board_size, height=board_size, n_in_row=4)
24 | board_init.init_board()
25 |
26 | for x,y in x_stones:
27 | location = [x-1,y-1]
28 | board_init.update(1, location)
29 | for x,y in o_stones:
30 | location = [x-1, y-1]
31 | board_init.update(2, location)
32 | print board_init.availables
33 | print board_init.states
34 |
35 | ai = MCTS(board_init, [2, 1], n_in_row = 4, time = 10, max_actions = 3000)
36 | x,y = ai.get_action()
37 | # print ("AI move: %d, %d"%(x+1,y+1))
38 | return (x+1,y+1)
39 |
40 |
41 | # def get_action(board, play_turn, availables, calculation_time = 5):
42 | # if len(availables) == 1:
43 | # return availables[0]
44 | # plays = {}
45 | # wins = {}
46 | # simulations = 0
47 | # begin = time.time()
48 | # while time.time() - begin < calculation_time:
49 | # board_copy = copy.deepcopy(board)
50 | # play_turn_copy = copy.deepcopy(play_turn)
51 | # run_simulation(board_copy, play_turn_copy)
52 | # move = select_one_move()
53 | #
54 | #
55 | # def run_simulation(board, availables, play_turn):
56 | # plays = {}
57 | # wins = {}
58 | # visited_states = set()
59 | # winner = -1
60 | # expand = True
61 | #
62 | # max_actions = 1000
63 | # for t in range(1, max_actions + 1):
64 |
65 | class Board(object):
66 | """
67 | board for game
68 | """
69 |
70 | def __init__(self, **kwargs):
71 | self.width = int(kwargs.get('width', 7))
72 | self.height = int(kwargs.get('height', 7))
73 | self.states = {} # board states, key:(player, move), value: piece type
74 | self.n_in_row = int(kwargs.get('n_in_row', 5)) # need how many pieces in a row to win
75 |
76 | def init_board(self):
77 | # if self.width < self.n_in_row or self.height < self.n_in_row:
78 | # raise Exception('board width and height can not less than %d' % self.n_in_row)
79 |
80 | self.availables = list(range(self.width * self.height)) # available moves
81 |
82 | for m in self.availables:
83 | self.states[m] = -1
84 |
85 | def move_to_location(self, move):
86 | """
87 | 3*3 board's moves like:
88 | 6 7 8
89 | 3 4 5
90 | 0 1 2
91 | and move 5's location is (1,2)
92 | """
93 | h = move // self.width
94 | w = move % self.width
95 | return [h, w]
96 |
97 | def location_to_move(self, location):
98 | # if (len(location) != 2):
99 | # return -1
100 | h = location[0]
101 | w = location[1]
102 | move = h * self.width + w
103 | if (move not in range(self.width * self.height)):
104 | return -1
105 | return move
106 |
107 | def update(self, player, location):
108 | move = self.location_to_move(location)
109 | self.states[move] = player
110 | self.availables.remove(move)
111 |
112 |
113 | class MCTS(object):
114 | """
115 | AI player, UCT RAVE
116 | """
117 |
118 | def __init__(self, board, play_turn, n_in_row=4, time=5, max_actions=1000):
119 | self.board = board
120 | self.play_turn = play_turn
121 | self.calculation_time = float(time)
122 | self.max_actions = max_actions
123 | self.n_in_row = n_in_row
124 |
125 | self.player = play_turn[0] # AI is first at now
126 | self.confident = 1.96
127 | self.equivalence = 10000 # calc beta
128 | self.max_depth = 1
129 |
130 | def get_action(self):
131 | if len(self.board.availables) == 1:
132 | return self.board.availables[0]
133 |
134 | self.plays = {} # key:(player, move), value:visited times
135 | self.wins = {} # key:(player, move), value:win times
136 | self.plays_rave = {} # key:move, value:visited times
137 | self.wins_rave = {} # key:move, value:{player: win times}
138 | simulations = 0
139 | begin = time.time()
140 | while time.time() - begin < self.calculation_time:
141 | board_copy = copy.deepcopy(self.board) # simulation will change board's states,
142 | play_turn_copy = copy.deepcopy(self.play_turn) # and play turn
143 | self.run_simulation(board_copy, play_turn_copy)
144 | simulations += 1
145 | print(simulations)
146 |
147 | move = self.select_one_move()
148 | location = self.board.move_to_location(move)
149 | return location
150 |
151 | def run_simulation(self, board, play_turn):
152 | """
153 | MCTS main process
154 | """
155 |
156 | plays = self.plays
157 | wins = self.wins
158 | plays_rave = self.plays_rave
159 | wins_rave = self.wins_rave
160 | availables = board.availables
161 |
162 | player = self.get_player(play_turn)
163 | visited_states = set()
164 | winner = -1
165 | expand = True
166 | # Simulation
167 | for t in range(1, self.max_actions + 1):
168 | # Selection
169 | # if all moves have statistics info, choose one that have max UCB value
170 | if all(plays.get((player, move)) for move in availables):
171 | value, move = max(
172 | ((1 - sqrt(self.equivalence / (3 * plays_rave[move] + self.equivalence))) * (
173 | wins[(player, move)] / plays[(player, move)]) +
174 | sqrt(self.equivalence / (3 * plays_rave[move] + self.equivalence)) * (
175 | wins_rave[move][player] / plays_rave[move]) +
176 | sqrt(self.confident * log(plays_rave[move]) / plays[(player, move)]), move)
177 | for move in availables) # UCT RAVE
178 | else:
179 | # a simple strategy
180 | # prefer to choose the nearer moves without statistics,
181 | # and then the farthers.
182 | # try ro add statistics info to all moves quickly
183 | adjacents = []
184 | if len(availables) > self.n_in_row:
185 | adjacents = self.adjacent_moves(board, player, plays)
186 |
187 | if len(adjacents):
188 | move = choice(adjacents)
189 | else:
190 | peripherals = []
191 | for move in availables:
192 | if not plays.get((player, move)):
193 | peripherals.append(move)
194 | move = choice(peripherals)
195 | h = move // board.width
196 | w = move % board.width
197 | location = [h, w]
198 | board.update(player, location)
199 |
200 | # Expand
201 | # add only one new child node each time
202 | if expand and (player, move) not in plays:
203 | expand = False
204 | plays[(player, move)] = 0
205 | wins[(player, move)] = 0
206 | if move not in plays_rave:
207 | plays_rave[move] = 0
208 | if move in wins_rave:
209 | wins_rave[move][player] = 0
210 | else:
211 | wins_rave[move] = {player: 0}
212 | if t > self.max_depth:
213 | self.max_depth = t
214 |
215 | visited_states.add((player, move))
216 |
217 | is_full = not len(availables)
218 | win, winner = self.has_a_winner(board)
219 | if is_full or win:
220 | break
221 |
222 | player = self.get_player(play_turn)
223 |
224 | # Back-propagation
225 | for player, move in visited_states:
226 | if (player, move) in plays:
227 | plays[(player, move)] += 1 # all visited moves
228 | if player == winner:
229 | wins[(player, move)] += 1 # only winner's moves
230 | if move in plays_rave:
231 | plays_rave[move] += 1 # no matter which player
232 | if winner in wins_rave[move]:
233 | wins_rave[move][winner] += 1 # each move and every player
234 |
235 | def get_player(self, players):
236 | p = players.pop(0)
237 | players.append(p)
238 | return p
239 |
240 | def select_one_move(self):
241 | # percent_wins, move = max(
242 | # (self.wins.get((self.player, move), 0) /
243 | # self.plays.get((self.player, move), 1),
244 | # move)
245 | # for move in self.board.availables)
246 | moves = {}
247 | for move in self.board.availables:
248 | # print('%f %d' % (100*self.wins.get((self.player, move), 0)/self.plays.get((self.player, move), 1), move))
249 | moves[move] = 100*self.wins.get((self.player, move), 0)/self.plays.get((self.player, move), 1)
250 | comb = max(zip(moves.values(), moves.keys()))
251 | move = comb[1]
252 | print(move)
253 |
254 |
255 | # Display the statistics for each possible play,
256 | # first is MC value, second is AMAF value
257 | for x in sorted(
258 | ((100 * self.wins.get((self.player, move), 0) /
259 | self.plays.get((self.player, move), 1),
260 | 100 * self.wins_rave.get(move, {}).get(self.player, 0) /
261 | self.plays_rave.get(move, 1),
262 | self.wins.get((self.player, move), 0),
263 | self.plays.get((self.player, move), 0),
264 | self.wins_rave.get(move, {}).get(self.player, 0),
265 | self.plays_rave.get(move, 1),
266 | self.board.move_to_location(move))
267 | for move in self.board.availables),
268 | reverse=True):
269 | print('{6}: {0:.2f}%--{1:.2f}% ({2} / {3})--({4} / {5})'.format(*x))
270 | print(move)
271 | return move
272 |
273 | def adjacent_moves(self, board, player, plays):
274 | """
275 | adjacent moves without statistics info
276 | """
277 | moved = list(set(range(board.width * board.height)) - set(board.availables))
278 | adjacents = set()
279 | width = board.width
280 | height = board.height
281 |
282 | for m in moved:
283 | h = m // width
284 | w = m % width
285 | if w < width - 1:
286 | adjacents.add(m + 1) # right
287 | if w > 0:
288 | adjacents.add(m - 1) # left
289 | if h < height - 1:
290 | adjacents.add(m + width) # upper
291 | if h > 0:
292 | adjacents.add(m - width) # lower
293 | if w < width - 1 and h < height - 1:
294 | adjacents.add(m + width + 1) # upper right
295 | if w > 0 and h < height - 1:
296 | adjacents.add(m + width - 1) # upper left
297 | if w < width - 1 and h > 0:
298 | adjacents.add(m - width + 1) # lower right
299 | if w > 0 and h > 0:
300 | adjacents.add(m - width - 1) # lower left
301 |
302 | adjacents = list(set(adjacents) - set(moved))
303 | for move in adjacents:
304 | if plays.get((player, move)):
305 | adjacents.remove(move)
306 | return adjacents
307 |
308 | def has_a_winner(self, board):
309 | moved = list(set(range(board.width * board.height)) - set(board.availables))
310 | if (len(moved) < self.n_in_row + 2):
311 | return False, -1
312 |
313 | width = board.width
314 | height = board.height
315 | states = board.states
316 | n = self.n_in_row
317 | for m in moved:
318 | h = m // width
319 | w = m % width
320 | player = states[m]
321 |
322 | if (w in range(width - n + 1) and
323 | len(set(states[i] for i in range(m, m + n))) == 1):
324 | return True, player
325 |
326 | if (h in range(height - n + 1) and
327 | len(set(states[i] for i in range(m, m + n * width, width))) == 1):
328 | return True, player
329 |
330 | if (w in range(width - n + 1) and h in range(height - n + 1) and
331 | len(set(states[i] for i in range(m, m + n * (width + 1), width + 1))) == 1):
332 | return True, player
333 |
334 | if (w in range(n - 1, width) and h in range(height - n + 1) and
335 | len(set(states[i] for i in range(m, m + n * (width - 1), width - 1))) == 1):
336 | return True, player
337 |
338 | return False, -1
339 |
340 | def __str__(self):
341 | return "AI"
342 |
343 | # class Game(object):
344 | # """
345 | # game server
346 | # """
347 | #
348 | # def __init__(self, board, **kwargs):
349 | # self.board = board
350 | # self.player = [1, 2] # player1 and player2
351 | # self.n_in_row = int(kwargs.get('n_in_row', 5))
352 | # self.time = float(kwargs.get('time', 5))
353 | # self.max_actions = int(kwargs.get('max_actions', 1000))
354 | #
355 | # def start(self):
356 | # p1, p2 = self.init_player()
357 | # self.board.init_board()
358 | #
359 | # ai = MCTS(self.board, [p1, p2], self.n_in_row, self.time, self.max_actions)
360 | # human = Human(self.board, p2)
361 | # players = {}
362 | # players[p1] = ai
363 | # players[p2] = human
364 | # turn = [p1, p2]
365 | # shuffle(turn)
366 | # self.graphic(self.board, human, ai)
367 | # while (1):
368 | # p = turn.pop(0)
369 | # turn.append(p)
370 | # player_in_turn = players[p]
371 | # move = player_in_turn.get_action()
372 | # self.board.update(p, move)
373 | # self.graphic(self.board, human, ai)
374 | # end, winner = self.game_end(ai)
375 | # if end:
376 | # if winner != -1:
377 | # print("Game end. Winner is", players[winner])
378 | # break
379 | #
380 | # def init_player(self):
381 | # plist = list(range(len(self.player)))
382 | # index1 = choice(plist)
383 | # plist.remove(index1)
384 | # index2 = choice(plist)
385 | #
386 | # return self.player[index1], self.player[index2]
387 | #
388 | # def game_end(self, ai):
389 | # win, winner = ai.has_a_winner(self.board)
390 | # if win:
391 | # return True, winner
392 | # elif not len(self.board.availables):
393 | # print("Game end. Tie")
394 | # return True, -1
395 | # return False, -1
396 |
397 |
398 | def finish():
399 | pass
400 |
--------------------------------------------------------------------------------
/report/acl2017.sty:
--------------------------------------------------------------------------------
1 | % 2017: modified to support DOI links in bibliography. Now uses
2 | % natbib package rather than defining citation commands in this file.
3 | % Use with acl_natbib.bst bib style. -- Dan Gildea
4 |
5 | % This is the LaTeX style for ACL 2016. It contains Margaret Mitchell's
6 | % line number adaptations (ported by Hai Zhao and Yannick Versley).
7 |
8 | % It is nearly identical to the style files for ACL 2015,
9 | % ACL 2014, EACL 2006, ACL2005, ACL 2002, ACL 2001, ACL 2000,
10 | % EACL 95 and EACL 99.
11 | %
12 | % Changes made include: adapt layout to A4 and centimeters, widen abstract
13 |
14 | % This is the LaTeX style file for ACL 2000. It is nearly identical to the
15 | % style files for EACL 95 and EACL 99. Minor changes include editing the
16 | % instructions to reflect use of \documentclass rather than \documentstyle
17 | % and removing the white space before the title on the first page
18 | % -- John Chen, June 29, 2000
19 |
20 | % This is the LaTeX style file for EACL-95. It is identical to the
21 | % style file for ANLP '94 except that the margins are adjusted for A4
22 | % paper. -- abney 13 Dec 94
23 |
24 | % The ANLP '94 style file is a slightly modified
25 | % version of the style used for AAAI and IJCAI, using some changes
26 | % prepared by Fernando Pereira and others and some minor changes
27 | % by Paul Jacobs.
28 |
29 | % Papers prepared using the aclsub.sty file and acl.bst bibtex style
30 | % should be easily converted to final format using this style.
31 | % (1) Submission information (\wordcount, \subject, and \makeidpage)
32 | % should be removed.
33 | % (2) \summary should be removed. The summary material should come
34 | % after \maketitle and should be in the ``abstract'' environment
35 | % (between \begin{abstract} and \end{abstract}).
36 | % (3) Check all citations. This style should handle citations correctly
37 | % and also allows multiple citations separated by semicolons.
38 | % (4) Check figures and examples. Because the final format is double-
39 | % column, some adjustments may have to be made to fit text in the column
40 | % or to choose full-width (\figure*} figures.
41 |
42 | % Place this in a file called aclap.sty in the TeX search path.
43 | % (Placing it in the same directory as the paper should also work.)
44 |
45 | % Prepared by Peter F. Patel-Schneider, liberally using the ideas of
46 | % other style hackers, including Barbara Beeton.
47 | % This style is NOT guaranteed to work. It is provided in the hope
48 | % that it will make the preparation of papers easier.
49 | %
50 | % There are undoubtably bugs in this style. If you make bug fixes,
51 | % improvements, etc. please let me know. My e-mail address is:
52 | % pfps@research.att.com
53 |
54 | % Papers are to be prepared using the ``acl_natbib'' bibliography style,
55 | % as follows:
56 | % \documentclass[11pt]{article}
57 | % \usepackage{acl2000}
58 | % \title{Title}
59 | % \author{Author 1 \and Author 2 \\ Address line \\ Address line \And
60 | % Author 3 \\ Address line \\ Address line}
61 | % \begin{document}
62 | % ...
63 | % \bibliography{bibliography-file}
64 | % \bibliographystyle{acl_natbib}
65 | % \end{document}
66 |
67 | % Author information can be set in various styles:
68 | % For several authors from the same institution:
69 | % \author{Author 1 \and ... \and Author n \\
70 | % Address line \\ ... \\ Address line}
71 | % if the names do not fit well on one line use
72 | % Author 1 \\ {\bf Author 2} \\ ... \\ {\bf Author n} \\
73 | % For authors from different institutions:
74 | % \author{Author 1 \\ Address line \\ ... \\ Address line
75 | % \And ... \And
76 | % Author n \\ Address line \\ ... \\ Address line}
77 | % To start a seperate ``row'' of authors use \AND, as in
78 | % \author{Author 1 \\ Address line \\ ... \\ Address line
79 | % \AND
80 | % Author 2 \\ Address line \\ ... \\ Address line \And
81 | % Author 3 \\ Address line \\ ... \\ Address line}
82 |
83 | % If the title and author information does not fit in the area allocated,
84 | % place \setlength\titlebox{} right after
85 | % \usepackage{acl2015}
86 | % where can be something larger than 5cm
87 |
88 | % include hyperref, unless user specifies nohyperref option like this:
89 | % \usepackage[nohyperref]{acl2017}
90 | \newif\ifacl@hyperref
91 | \DeclareOption{hyperref}{\acl@hyperreftrue}
92 | \DeclareOption{nohyperref}{\acl@hyperreffalse}
93 | \ExecuteOptions{hyperref} % default is to use hyperref
94 | \ProcessOptions\relax
95 | \ifacl@hyperref
96 | \RequirePackage{hyperref}
97 | \usepackage{xcolor} % make links dark blue
98 | \definecolor{darkblue}{rgb}{0, 0, 0.5}
99 | \hypersetup{colorlinks=true,citecolor=darkblue, linkcolor=darkblue, urlcolor=darkblue}
100 | \else
101 | % This definition is used if the hyperref package is not loaded.
102 | % It provides a backup, no-op definiton of \href.
103 | % This is necessary because \href command is used in the acl_natbib.bst file.
104 | \def\href#1#2{{#2}}
105 | \fi
106 |
107 | \typeout{Conference Style for ACL 2017}
108 |
109 | % NOTE: Some laser printers have a serious problem printing TeX output.
110 | % These printing devices, commonly known as ``write-white'' laser
111 | % printers, tend to make characters too light. To get around this
112 | % problem, a darker set of fonts must be created for these devices.
113 | %
114 |
115 | \newcommand{\Thanks}[1]{\thanks{\ #1}}
116 |
117 | % A4 modified by Eneko; again modified by Alexander for 5cm titlebox
118 | \setlength{\paperwidth}{21cm} % A4
119 | \setlength{\paperheight}{29.7cm}% A4
120 | \setlength\topmargin{-0.5cm}
121 | \setlength\oddsidemargin{0cm}
122 | \setlength\textheight{24.7cm}
123 | \setlength\textwidth{16.0cm}
124 | \setlength\columnsep{0.6cm}
125 | \newlength\titlebox
126 | \setlength\titlebox{5cm}
127 | \setlength\headheight{5pt}
128 | \setlength\headsep{0pt}
129 | \thispagestyle{empty}
130 | \pagestyle{empty}
131 |
132 |
133 | \flushbottom \twocolumn \sloppy
134 |
135 | % We're never going to need a table of contents, so just flush it to
136 | % save space --- suggested by drstrip@sandia-2
137 | \def\addcontentsline#1#2#3{}
138 |
139 | \newif\ifaclfinal
140 | \aclfinalfalse
141 | \def\aclfinalcopy{\global\aclfinaltrue}
142 |
143 | %% ----- Set up hooks to repeat content on every page of the output doc,
144 | %% necessary for the line numbers in the submitted version. --MM
145 | %%
146 | %% Copied from CVPR 2015's cvpr_eso.sty, which appears to be largely copied from everyshi.sty.
147 | %%
148 | %% Original cvpr_eso.sty available at: http://www.pamitc.org/cvpr15/author_guidelines.php
149 | %% Original evershi.sty available at: https://www.ctan.org/pkg/everyshi
150 | %%
151 | %% Copyright (C) 2001 Martin Schr\"oder:
152 | %%
153 | %% Martin Schr"oder
154 | %% Cr"usemannallee 3
155 | %% D-28213 Bremen
156 | %% Martin.Schroeder@ACM.org
157 | %%
158 | %% This program may be redistributed and/or modified under the terms
159 | %% of the LaTeX Project Public License, either version 1.0 of this
160 | %% license, or (at your option) any later version.
161 | %% The latest version of this license is in
162 | %% CTAN:macros/latex/base/lppl.txt.
163 | %%
164 | %% Happy users are requested to send [Martin] a postcard. :-)
165 | %%
166 | \newcommand{\@EveryShipoutACL@Hook}{}
167 | \newcommand{\@EveryShipoutACL@AtNextHook}{}
168 | \newcommand*{\EveryShipoutACL}[1]
169 | {\g@addto@macro\@EveryShipoutACL@Hook{#1}}
170 | \newcommand*{\AtNextShipoutACL@}[1]
171 | {\g@addto@macro\@EveryShipoutACL@AtNextHook{#1}}
172 | \newcommand{\@EveryShipoutACL@Shipout}{%
173 | \afterassignment\@EveryShipoutACL@Test
174 | \global\setbox\@cclv= %
175 | }
176 | \newcommand{\@EveryShipoutACL@Test}{%
177 | \ifvoid\@cclv\relax
178 | \aftergroup\@EveryShipoutACL@Output
179 | \else
180 | \@EveryShipoutACL@Output
181 | \fi%
182 | }
183 | \newcommand{\@EveryShipoutACL@Output}{%
184 | \@EveryShipoutACL@Hook%
185 | \@EveryShipoutACL@AtNextHook%
186 | \gdef\@EveryShipoutACL@AtNextHook{}%
187 | \@EveryShipoutACL@Org@Shipout\box\@cclv%
188 | }
189 | \newcommand{\@EveryShipoutACL@Org@Shipout}{}
190 | \newcommand*{\@EveryShipoutACL@Init}{%
191 | \message{ABD: EveryShipout initializing macros}%
192 | \let\@EveryShipoutACL@Org@Shipout\shipout
193 | \let\shipout\@EveryShipoutACL@Shipout
194 | }
195 | \AtBeginDocument{\@EveryShipoutACL@Init}
196 |
197 | %% ----- Set up for placing additional items into the submitted version --MM
198 | %%
199 | %% Based on eso-pic.sty
200 | %%
201 | %% Original available at: https://www.ctan.org/tex-archive/macros/latex/contrib/eso-pic
202 | %% Copyright (C) 1998-2002 by Rolf Niepraschk
203 | %%
204 | %% Which may be distributed and/or modified under the conditions of
205 | %% the LaTeX Project Public License, either version 1.2 of this license
206 | %% or (at your option) any later version. The latest version of this
207 | %% license is in:
208 | %%
209 | %% http://www.latex-project.org/lppl.txt
210 | %%
211 | %% and version 1.2 or later is part of all distributions of LaTeX version
212 | %% 1999/12/01 or later.
213 | %%
214 | %% In contrast to the original, we do not include the definitions for/using:
215 | %% gridpicture, div[2], isMEMOIR[1], gridSetup[6][], subgridstyle{dotted}, labelfactor{}, gap{}, gridunitname{}, gridunit{}, gridlines{\thinlines}, subgridlines{\thinlines}, the {keyval} package, evenside margin, nor any definitions with 'color'.
216 | %%
217 | %% These are beyond what is needed for the NAACL style.
218 | %%
219 | \newcommand\LenToUnit[1]{#1\@gobble}
220 | \newcommand\AtPageUpperLeft[1]{%
221 | \begingroup
222 | \@tempdima=0pt\relax\@tempdimb=\ESO@yoffsetI\relax
223 | \put(\LenToUnit{\@tempdima},\LenToUnit{\@tempdimb}){#1}%
224 | \endgroup
225 | }
226 | \newcommand\AtPageLowerLeft[1]{\AtPageUpperLeft{%
227 | \put(0,\LenToUnit{-\paperheight}){#1}}}
228 | \newcommand\AtPageCenter[1]{\AtPageUpperLeft{%
229 | \put(\LenToUnit{.5\paperwidth},\LenToUnit{-.5\paperheight}){#1}}}
230 | \newcommand\AtPageLowerCenter[1]{\AtPageUpperLeft{%
231 | \put(\LenToUnit{.5\paperwidth},\LenToUnit{-\paperheight}){#1}}}%
232 | \newcommand\AtPageLowishCenter[1]{\AtPageUpperLeft{%
233 | \put(\LenToUnit{.5\paperwidth},\LenToUnit{-.96\paperheight}){#1}}}
234 | \newcommand\AtTextUpperLeft[1]{%
235 | \begingroup
236 | \setlength\@tempdima{1in}%
237 | \advance\@tempdima\oddsidemargin%
238 | \@tempdimb=\ESO@yoffsetI\relax\advance\@tempdimb-1in\relax%
239 | \advance\@tempdimb-\topmargin%
240 | \advance\@tempdimb-\headheight\advance\@tempdimb-\headsep%
241 | \put(\LenToUnit{\@tempdima},\LenToUnit{\@tempdimb}){#1}%
242 | \endgroup
243 | }
244 | \newcommand\AtTextLowerLeft[1]{\AtTextUpperLeft{%
245 | \put(0,\LenToUnit{-\textheight}){#1}}}
246 | \newcommand\AtTextCenter[1]{\AtTextUpperLeft{%
247 | \put(\LenToUnit{.5\textwidth},\LenToUnit{-.5\textheight}){#1}}}
248 | \newcommand{\ESO@HookI}{} \newcommand{\ESO@HookII}{}
249 | \newcommand{\ESO@HookIII}{}
250 | \newcommand{\AddToShipoutPicture}{%
251 | \@ifstar{\g@addto@macro\ESO@HookII}{\g@addto@macro\ESO@HookI}}
252 | \newcommand{\ClearShipoutPicture}{\global\let\ESO@HookI\@empty}
253 | \newcommand{\@ShipoutPicture}{%
254 | \bgroup
255 | \@tempswafalse%
256 | \ifx\ESO@HookI\@empty\else\@tempswatrue\fi%
257 | \ifx\ESO@HookII\@empty\else\@tempswatrue\fi%
258 | \ifx\ESO@HookIII\@empty\else\@tempswatrue\fi%
259 | \if@tempswa%
260 | \@tempdima=1in\@tempdimb=-\@tempdima%
261 | \advance\@tempdimb\ESO@yoffsetI%
262 | \unitlength=1pt%
263 | \global\setbox\@cclv\vbox{%
264 | \vbox{\let\protect\relax
265 | \pictur@(0,0)(\strip@pt\@tempdima,\strip@pt\@tempdimb)%
266 | \ESO@HookIII\ESO@HookI\ESO@HookII%
267 | \global\let\ESO@HookII\@empty%
268 | \endpicture}%
269 | \nointerlineskip%
270 | \box\@cclv}%
271 | \fi
272 | \egroup
273 | }
274 | \EveryShipoutACL{\@ShipoutPicture}
275 | \newif\ifESO@dvips\ESO@dvipsfalse
276 | \newif\ifESO@grid\ESO@gridfalse
277 | \newif\ifESO@texcoord\ESO@texcoordfalse
278 | \newcommand*\ESO@griddelta{}\newcommand*\ESO@griddeltaY{}
279 | \newcommand*\ESO@gridDelta{}\newcommand*\ESO@gridDeltaY{}
280 | \newcommand*\ESO@yoffsetI{}\newcommand*\ESO@yoffsetII{}
281 | \ifESO@texcoord
282 | \def\ESO@yoffsetI{0pt}\def\ESO@yoffsetII{-\paperheight}
283 | \edef\ESO@griddeltaY{-\ESO@griddelta}\edef\ESO@gridDeltaY{-\ESO@gridDelta}
284 | \else
285 | \def\ESO@yoffsetI{\paperheight}\def\ESO@yoffsetII{0pt}
286 | \edef\ESO@griddeltaY{\ESO@griddelta}\edef\ESO@gridDeltaY{\ESO@gridDelta}
287 | \fi
288 |
289 |
290 | %% ----- Submitted version markup: Page numbers, ruler, and confidentiality. Using ideas/code from cvpr.sty 2015. --MM
291 |
292 | \font\naaclhv = phvb at 8pt
293 |
294 | %% Define vruler %%
295 |
296 | %\makeatletter
297 | \newbox\aclrulerbox
298 | \newcount\aclrulercount
299 | \newdimen\aclruleroffset
300 | \newdimen\cv@lineheight
301 | \newdimen\cv@boxheight
302 | \newbox\cv@tmpbox
303 | \newcount\cv@refno
304 | \newcount\cv@tot
305 | % NUMBER with left flushed zeros \fillzeros[]
306 | \newcount\cv@tmpc@ \newcount\cv@tmpc
307 | \def\fillzeros[#1]#2{\cv@tmpc@=#2\relax\ifnum\cv@tmpc@<0\cv@tmpc@=-\cv@tmpc@\fi
308 | \cv@tmpc=1 %
309 | \loop\ifnum\cv@tmpc@<10 \else \divide\cv@tmpc@ by 10 \advance\cv@tmpc by 1 \fi
310 | \ifnum\cv@tmpc@=10\relax\cv@tmpc@=11\relax\fi \ifnum\cv@tmpc@>10 \repeat
311 | \ifnum#2<0\advance\cv@tmpc1\relax-\fi
312 | \loop\ifnum\cv@tmpc<#1\relax0\advance\cv@tmpc1\relax\fi \ifnum\cv@tmpc<#1 \repeat
313 | \cv@tmpc@=#2\relax\ifnum\cv@tmpc@<0\cv@tmpc@=-\cv@tmpc@\fi \relax\the\cv@tmpc@}%
314 | % \makevruler[][][][][]
315 | \def\makevruler[#1][#2][#3][#4][#5]{\begingroup\offinterlineskip
316 | \textheight=#5\vbadness=10000\vfuzz=120ex\overfullrule=0pt%
317 | \global\setbox\aclrulerbox=\vbox to \textheight{%
318 | {\parskip=0pt\hfuzz=150em\cv@boxheight=\textheight
319 | \cv@lineheight=#1\global\aclrulercount=#2%
320 | \cv@tot\cv@boxheight\divide\cv@tot\cv@lineheight\advance\cv@tot2%
321 | \cv@refno1\vskip-\cv@lineheight\vskip1ex%
322 | \loop\setbox\cv@tmpbox=\hbox to0cm{{\naaclhv\hfil\fillzeros[#4]\aclrulercount}}%
323 | \ht\cv@tmpbox\cv@lineheight\dp\cv@tmpbox0pt\box\cv@tmpbox\break
324 | \advance\cv@refno1\global\advance\aclrulercount#3\relax
325 | \ifnum\cv@refno<\cv@tot\repeat}}\endgroup}%
326 | %\makeatother
327 |
328 |
329 | \def\aclpaperid{***}
330 | \def\confidential{ACL 2017 Submission~\aclpaperid. Confidential Review Copy. DO NOT DISTRIBUTE.}
331 |
332 | %% Page numbering, Vruler and Confidentiality %%
333 | % \makevruler[][][][][]
334 | \def\aclruler#1{\makevruler[14.17pt][#1][1][3][\textheight]\usebox{\aclrulerbox}}
335 | \def\leftoffset{-2.1cm} %original: -45pt
336 | \def\rightoffset{17.5cm} %original: 500pt
337 | \ifaclfinal\else\pagenumbering{arabic}
338 | \AddToShipoutPicture{%
339 | \ifaclfinal\else
340 | \AtPageLowishCenter{\thepage}
341 | \aclruleroffset=\textheight
342 | \advance\aclruleroffset4pt
343 | \AtTextUpperLeft{%
344 | \put(\LenToUnit{\leftoffset},\LenToUnit{-\aclruleroffset}){%left ruler
345 | \aclruler{\aclrulercount}}
346 | \put(\LenToUnit{\rightoffset},\LenToUnit{-\aclruleroffset}){%right ruler
347 | \aclruler{\aclrulercount}}
348 | }
349 | \AtTextUpperLeft{%confidential
350 | \put(0,\LenToUnit{1cm}){\parbox{\textwidth}{\centering\naaclhv\confidential}}
351 | }
352 | \fi
353 | }
354 |
355 | %%%% ----- End settings for placing additional items into the submitted version --MM ----- %%%%
356 |
357 | %%%% ----- Begin settings for both submitted and camera-ready version ----- %%%%
358 |
359 | %% Title and Authors %%
360 |
361 | \newcommand\outauthor{
362 | \begin{tabular}[t]{c}
363 | \ifaclfinal
364 | \bf\@author
365 | \else
366 | % Avoiding common accidental de-anonymization issue. --MM
367 | \bf Anonymous ACL submission
368 | \fi
369 | \end{tabular}}
370 |
371 | % Changing the expanded titlebox for submissions to 2.5 in (rather than 6.5cm)
372 | % and moving it to the style sheet, rather than within the example tex file. --MM
373 | \ifaclfinal
374 | \else
375 | \addtolength\titlebox{.25in}
376 | \fi
377 | % Mostly taken from deproc.
378 | \def\maketitle{\par
379 | \begingroup
380 | \def\thefootnote{\fnsymbol{footnote}}
381 | \def\@makefnmark{\hbox to 0pt{$^{\@thefnmark}$\hss}}
382 | \twocolumn[\@maketitle] \@thanks
383 | \endgroup
384 | \setcounter{footnote}{0}
385 | \let\maketitle\relax \let\@maketitle\relax
386 | \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\let\thanks\relax}
387 | \def\@maketitle{\vbox to \titlebox{\hsize\textwidth
388 | \linewidth\hsize \vskip 0.125in minus 0.125in \centering
389 | {\Large\bf \@title \par} \vskip 0.2in plus 1fil minus 0.1in
390 | {\def\and{\unskip\enspace{\rm and}\enspace}%
391 | \def\And{\end{tabular}\hss \egroup \hskip 1in plus 2fil
392 | \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\bf}%
393 | \def\AND{\end{tabular}\hss\egroup \hfil\hfil\egroup
394 | \vskip 0.25in plus 1fil minus 0.125in
395 | \hbox to \linewidth\bgroup\large \hfil\hfil
396 | \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\bf}
397 | \hbox to \linewidth\bgroup\large \hfil\hfil
398 | \hbox to 0pt\bgroup\hss
399 | \outauthor
400 | \hss\egroup
401 | \hfil\hfil\egroup}
402 | \vskip 0.3in plus 2fil minus 0.1in
403 | }}
404 |
405 | % margins for abstract
406 | \renewenvironment{abstract}%
407 | {\centerline{\large\bf Abstract}%
408 | \begin{list}{}%
409 | {\setlength{\rightmargin}{0.6cm}%
410 | \setlength{\leftmargin}{0.6cm}}%
411 | \item[]\ignorespaces}%
412 | {\unskip\end{list}}
413 |
414 | %\renewenvironment{abstract}{\centerline{\large\bf
415 | % Abstract}\vspace{0.5ex}\begin{quote}}{\par\end{quote}\vskip 1ex}
416 |
417 | \RequirePackage{natbib}
418 | % for citation commands in the .tex, authors can use:
419 | % \citep, \citet, and \citeyearpar for compatibility with natbib, or
420 | % \cite, \newcite, and \shortcite for compatibility with older ACL .sty files
421 | \renewcommand\cite{\citep} % to get "(Author Year)" with natbib
422 | \newcommand\shortcite{\citeyearpar}% to get "(Year)" with natbib
423 | \newcommand\newcite{\citet} % to get "Author (Year)" with natbib
424 |
425 |
426 | % bibliography
427 |
428 | \def\@up#1{\raise.2ex\hbox{#1}}
429 |
430 | % Don't put a label in the bibliography at all. Just use the unlabeled format
431 | % instead.
432 | \def\thebibliography#1{\vskip\parskip%
433 | \vskip\baselineskip%
434 | \def\baselinestretch{1}%
435 | \ifx\@currsize\normalsize\@normalsize\else\@currsize\fi%
436 | \vskip-\parskip%
437 | \vskip-\baselineskip%
438 | \section*{References\@mkboth
439 | {References}{References}}\list
440 | {}{\setlength{\labelwidth}{0pt}\setlength{\leftmargin}{\parindent}
441 | \setlength{\itemindent}{-\parindent}}
442 | \def\newblock{\hskip .11em plus .33em minus -.07em}
443 | \sloppy\clubpenalty4000\widowpenalty4000
444 | \sfcode`\.=1000\relax}
445 | \let\endthebibliography=\endlist
446 |
447 |
448 | % Allow for a bibliography of sources of attested examples
449 | \def\thesourcebibliography#1{\vskip\parskip%
450 | \vskip\baselineskip%
451 | \def\baselinestretch{1}%
452 | \ifx\@currsize\normalsize\@normalsize\else\@currsize\fi%
453 | \vskip-\parskip%
454 | \vskip-\baselineskip%
455 | \section*{Sources of Attested Examples\@mkboth
456 | {Sources of Attested Examples}{Sources of Attested Examples}}\list
457 | {}{\setlength{\labelwidth}{0pt}\setlength{\leftmargin}{\parindent}
458 | \setlength{\itemindent}{-\parindent}}
459 | \def\newblock{\hskip .11em plus .33em minus -.07em}
460 | \sloppy\clubpenalty4000\widowpenalty4000
461 | \sfcode`\.=1000\relax}
462 | \let\endthesourcebibliography=\endlist
463 |
464 | % sections with less space
465 | \def\section{\@startsection {section}{1}{\z@}{-2.0ex plus
466 | -0.5ex minus -.2ex}{1.5ex plus 0.3ex minus .2ex}{\large\bf\raggedright}}
467 | \def\subsection{\@startsection{subsection}{2}{\z@}{-1.8ex plus
468 | -0.5ex minus -.2ex}{0.8ex plus .2ex}{\normalsize\bf\raggedright}}
469 | %% changed by KO to - values to get teh initial parindent right
470 | \def\subsubsection{\@startsection{subsubsection}{3}{\z@}{-1.5ex plus
471 | -0.5ex minus -.2ex}{0.5ex plus .2ex}{\normalsize\bf\raggedright}}
472 | \def\paragraph{\@startsection{paragraph}{4}{\z@}{1.5ex plus
473 | 0.5ex minus .2ex}{-1em}{\normalsize\bf}}
474 | \def\subparagraph{\@startsection{subparagraph}{5}{\parindent}{1.5ex plus
475 | 0.5ex minus .2ex}{-1em}{\normalsize\bf}}
476 |
477 | % Footnotes
478 | \footnotesep 6.65pt %
479 | \skip\footins 9pt plus 4pt minus 2pt
480 | \def\footnoterule{\kern-3pt \hrule width 5pc \kern 2.6pt }
481 | \setcounter{footnote}{0}
482 |
483 | % Lists and paragraphs
484 | \parindent 1em
485 | \topsep 4pt plus 1pt minus 2pt
486 | \partopsep 1pt plus 0.5pt minus 0.5pt
487 | \itemsep 2pt plus 1pt minus 0.5pt
488 | \parsep 2pt plus 1pt minus 0.5pt
489 |
490 | \leftmargin 2em \leftmargini\leftmargin \leftmarginii 2em
491 | \leftmarginiii 1.5em \leftmarginiv 1.0em \leftmarginv .5em \leftmarginvi .5em
492 | \labelwidth\leftmargini\advance\labelwidth-\labelsep \labelsep 5pt
493 |
494 | \def\@listi{\leftmargin\leftmargini}
495 | \def\@listii{\leftmargin\leftmarginii
496 | \labelwidth\leftmarginii\advance\labelwidth-\labelsep
497 | \topsep 2pt plus 1pt minus 0.5pt
498 | \parsep 1pt plus 0.5pt minus 0.5pt
499 | \itemsep \parsep}
500 | \def\@listiii{\leftmargin\leftmarginiii
501 | \labelwidth\leftmarginiii\advance\labelwidth-\labelsep
502 | \topsep 1pt plus 0.5pt minus 0.5pt
503 | \parsep \z@ \partopsep 0.5pt plus 0pt minus 0.5pt
504 | \itemsep \topsep}
505 | \def\@listiv{\leftmargin\leftmarginiv
506 | \labelwidth\leftmarginiv\advance\labelwidth-\labelsep}
507 | \def\@listv{\leftmargin\leftmarginv
508 | \labelwidth\leftmarginv\advance\labelwidth-\labelsep}
509 | \def\@listvi{\leftmargin\leftmarginvi
510 | \labelwidth\leftmarginvi\advance\labelwidth-\labelsep}
511 |
512 | \abovedisplayskip 7pt plus2pt minus5pt%
513 | \belowdisplayskip \abovedisplayskip
514 | \abovedisplayshortskip 0pt plus3pt%
515 | \belowdisplayshortskip 4pt plus3pt minus3pt%
516 |
517 | % Less leading in most fonts (due to the narrow columns)
518 | % The choices were between 1-pt and 1.5-pt leading
519 | \def\@normalsize{\@setsize\normalsize{11pt}\xpt\@xpt}
520 | \def\small{\@setsize\small{10pt}\ixpt\@ixpt}
521 | \def\footnotesize{\@setsize\footnotesize{10pt}\ixpt\@ixpt}
522 | \def\scriptsize{\@setsize\scriptsize{8pt}\viipt\@viipt}
523 | \def\tiny{\@setsize\tiny{7pt}\vipt\@vipt}
524 | \def\large{\@setsize\large{14pt}\xiipt\@xiipt}
525 | \def\Large{\@setsize\Large{16pt}\xivpt\@xivpt}
526 | \def\LARGE{\@setsize\LARGE{20pt}\xviipt\@xviipt}
527 | \def\huge{\@setsize\huge{23pt}\xxpt\@xxpt}
528 | \def\Huge{\@setsize\Huge{28pt}\xxvpt\@xxvpt}
529 |
--------------------------------------------------------------------------------
/report/acl17-latex/acl2017.sty:
--------------------------------------------------------------------------------
1 | % 2017: modified to support DOI links in bibliography. Now uses
2 | % natbib package rather than defining citation commands in this file.
3 | % Use with acl_natbib.bst bib style. -- Dan Gildea
4 |
5 | % This is the LaTeX style for ACL 2016. It contains Margaret Mitchell's
6 | % line number adaptations (ported by Hai Zhao and Yannick Versley).
7 |
8 | % It is nearly identical to the style files for ACL 2015,
9 | % ACL 2014, EACL 2006, ACL2005, ACL 2002, ACL 2001, ACL 2000,
10 | % EACL 95 and EACL 99.
11 | %
12 | % Changes made include: adapt layout to A4 and centimeters, widen abstract
13 |
14 | % This is the LaTeX style file for ACL 2000. It is nearly identical to the
15 | % style files for EACL 95 and EACL 99. Minor changes include editing the
16 | % instructions to reflect use of \documentclass rather than \documentstyle
17 | % and removing the white space before the title on the first page
18 | % -- John Chen, June 29, 2000
19 |
20 | % This is the LaTeX style file for EACL-95. It is identical to the
21 | % style file for ANLP '94 except that the margins are adjusted for A4
22 | % paper. -- abney 13 Dec 94
23 |
24 | % The ANLP '94 style file is a slightly modified
25 | % version of the style used for AAAI and IJCAI, using some changes
26 | % prepared by Fernando Pereira and others and some minor changes
27 | % by Paul Jacobs.
28 |
29 | % Papers prepared using the aclsub.sty file and acl.bst bibtex style
30 | % should be easily converted to final format using this style.
31 | % (1) Submission information (\wordcount, \subject, and \makeidpage)
32 | % should be removed.
33 | % (2) \summary should be removed. The summary material should come
34 | % after \maketitle and should be in the ``abstract'' environment
35 | % (between \begin{abstract} and \end{abstract}).
36 | % (3) Check all citations. This style should handle citations correctly
37 | % and also allows multiple citations separated by semicolons.
38 | % (4) Check figures and examples. Because the final format is double-
39 | % column, some adjustments may have to be made to fit text in the column
40 | % or to choose full-width (\figure*} figures.
41 |
42 | % Place this in a file called aclap.sty in the TeX search path.
43 | % (Placing it in the same directory as the paper should also work.)
44 |
45 | % Prepared by Peter F. Patel-Schneider, liberally using the ideas of
46 | % other style hackers, including Barbara Beeton.
47 | % This style is NOT guaranteed to work. It is provided in the hope
48 | % that it will make the preparation of papers easier.
49 | %
50 | % There are undoubtably bugs in this style. If you make bug fixes,
51 | % improvements, etc. please let me know. My e-mail address is:
52 | % pfps@research.att.com
53 |
54 | % Papers are to be prepared using the ``acl_natbib'' bibliography style,
55 | % as follows:
56 | % \documentclass[11pt]{article}
57 | % \usepackage{acl2000}
58 | % \title{Title}
59 | % \author{Author 1 \and Author 2 \\ Address line \\ Address line \And
60 | % Author 3 \\ Address line \\ Address line}
61 | % \begin{document}
62 | % ...
63 | % \bibliography{bibliography-file}
64 | % \bibliographystyle{acl_natbib}
65 | % \end{document}
66 |
67 | % Author information can be set in various styles:
68 | % For several authors from the same institution:
69 | % \author{Author 1 \and ... \and Author n \\
70 | % Address line \\ ... \\ Address line}
71 | % if the names do not fit well on one line use
72 | % Author 1 \\ {\bf Author 2} \\ ... \\ {\bf Author n} \\
73 | % For authors from different institutions:
74 | % \author{Author 1 \\ Address line \\ ... \\ Address line
75 | % \And ... \And
76 | % Author n \\ Address line \\ ... \\ Address line}
77 | % To start a seperate ``row'' of authors use \AND, as in
78 | % \author{Author 1 \\ Address line \\ ... \\ Address line
79 | % \AND
80 | % Author 2 \\ Address line \\ ... \\ Address line \And
81 | % Author 3 \\ Address line \\ ... \\ Address line}
82 |
83 | % If the title and author information does not fit in the area allocated,
84 | % place \setlength\titlebox{} right after
85 | % \usepackage{acl2015}
86 | % where can be something larger than 5cm
87 |
88 | % include hyperref, unless user specifies nohyperref option like this:
89 | % \usepackage[nohyperref]{acl2017}
90 | \newif\ifacl@hyperref
91 | \DeclareOption{hyperref}{\acl@hyperreftrue}
92 | \DeclareOption{nohyperref}{\acl@hyperreffalse}
93 | \ExecuteOptions{hyperref} % default is to use hyperref
94 | \ProcessOptions\relax
95 | \ifacl@hyperref
96 | \RequirePackage{hyperref}
97 | \usepackage{xcolor} % make links dark blue
98 | \definecolor{darkblue}{rgb}{0, 0, 0.5}
99 | \hypersetup{colorlinks=true,citecolor=darkblue, linkcolor=darkblue, urlcolor=darkblue}
100 | \else
101 | % This definition is used if the hyperref package is not loaded.
102 | % It provides a backup, no-op definiton of \href.
103 | % This is necessary because \href command is used in the acl_natbib.bst file.
104 | \def\href#1#2{{#2}}
105 | \fi
106 |
107 | \typeout{Conference Style for ACL 2017}
108 |
109 | % NOTE: Some laser printers have a serious problem printing TeX output.
110 | % These printing devices, commonly known as ``write-white'' laser
111 | % printers, tend to make characters too light. To get around this
112 | % problem, a darker set of fonts must be created for these devices.
113 | %
114 |
115 | \newcommand{\Thanks}[1]{\thanks{\ #1}}
116 |
117 | % A4 modified by Eneko; again modified by Alexander for 5cm titlebox
118 | \setlength{\paperwidth}{21cm} % A4
119 | \setlength{\paperheight}{29.7cm}% A4
120 | \setlength\topmargin{-0.5cm}
121 | \setlength\oddsidemargin{0cm}
122 | \setlength\textheight{24.7cm}
123 | \setlength\textwidth{16.0cm}
124 | \setlength\columnsep{0.6cm}
125 | \newlength\titlebox
126 | \setlength\titlebox{5cm}
127 | \setlength\headheight{5pt}
128 | \setlength\headsep{0pt}
129 | \thispagestyle{empty}
130 | \pagestyle{empty}
131 |
132 |
133 | \flushbottom \twocolumn \sloppy
134 |
135 | % We're never going to need a table of contents, so just flush it to
136 | % save space --- suggested by drstrip@sandia-2
137 | \def\addcontentsline#1#2#3{}
138 |
139 | \newif\ifaclfinal
140 | \aclfinalfalse
141 | \def\aclfinalcopy{\global\aclfinaltrue}
142 |
143 | %% ----- Set up hooks to repeat content on every page of the output doc,
144 | %% necessary for the line numbers in the submitted version. --MM
145 | %%
146 | %% Copied from CVPR 2015's cvpr_eso.sty, which appears to be largely copied from everyshi.sty.
147 | %%
148 | %% Original cvpr_eso.sty available at: http://www.pamitc.org/cvpr15/author_guidelines.php
149 | %% Original evershi.sty available at: https://www.ctan.org/pkg/everyshi
150 | %%
151 | %% Copyright (C) 2001 Martin Schr\"oder:
152 | %%
153 | %% Martin Schr"oder
154 | %% Cr"usemannallee 3
155 | %% D-28213 Bremen
156 | %% Martin.Schroeder@ACM.org
157 | %%
158 | %% This program may be redistributed and/or modified under the terms
159 | %% of the LaTeX Project Public License, either version 1.0 of this
160 | %% license, or (at your option) any later version.
161 | %% The latest version of this license is in
162 | %% CTAN:macros/latex/base/lppl.txt.
163 | %%
164 | %% Happy users are requested to send [Martin] a postcard. :-)
165 | %%
166 | \newcommand{\@EveryShipoutACL@Hook}{}
167 | \newcommand{\@EveryShipoutACL@AtNextHook}{}
168 | \newcommand*{\EveryShipoutACL}[1]
169 | {\g@addto@macro\@EveryShipoutACL@Hook{#1}}
170 | \newcommand*{\AtNextShipoutACL@}[1]
171 | {\g@addto@macro\@EveryShipoutACL@AtNextHook{#1}}
172 | \newcommand{\@EveryShipoutACL@Shipout}{%
173 | \afterassignment\@EveryShipoutACL@Test
174 | \global\setbox\@cclv= %
175 | }
176 | \newcommand{\@EveryShipoutACL@Test}{%
177 | \ifvoid\@cclv\relax
178 | \aftergroup\@EveryShipoutACL@Output
179 | \else
180 | \@EveryShipoutACL@Output
181 | \fi%
182 | }
183 | \newcommand{\@EveryShipoutACL@Output}{%
184 | \@EveryShipoutACL@Hook%
185 | \@EveryShipoutACL@AtNextHook%
186 | \gdef\@EveryShipoutACL@AtNextHook{}%
187 | \@EveryShipoutACL@Org@Shipout\box\@cclv%
188 | }
189 | \newcommand{\@EveryShipoutACL@Org@Shipout}{}
190 | \newcommand*{\@EveryShipoutACL@Init}{%
191 | \message{ABD: EveryShipout initializing macros}%
192 | \let\@EveryShipoutACL@Org@Shipout\shipout
193 | \let\shipout\@EveryShipoutACL@Shipout
194 | }
195 | \AtBeginDocument{\@EveryShipoutACL@Init}
196 |
197 | %% ----- Set up for placing additional items into the submitted version --MM
198 | %%
199 | %% Based on eso-pic.sty
200 | %%
201 | %% Original available at: https://www.ctan.org/tex-archive/macros/latex/contrib/eso-pic
202 | %% Copyright (C) 1998-2002 by Rolf Niepraschk
203 | %%
204 | %% Which may be distributed and/or modified under the conditions of
205 | %% the LaTeX Project Public License, either version 1.2 of this license
206 | %% or (at your option) any later version. The latest version of this
207 | %% license is in:
208 | %%
209 | %% http://www.latex-project.org/lppl.txt
210 | %%
211 | %% and version 1.2 or later is part of all distributions of LaTeX version
212 | %% 1999/12/01 or later.
213 | %%
214 | %% In contrast to the original, we do not include the definitions for/using:
215 | %% gridpicture, div[2], isMEMOIR[1], gridSetup[6][], subgridstyle{dotted}, labelfactor{}, gap{}, gridunitname{}, gridunit{}, gridlines{\thinlines}, subgridlines{\thinlines}, the {keyval} package, evenside margin, nor any definitions with 'color'.
216 | %%
217 | %% These are beyond what is needed for the NAACL style.
218 | %%
219 | \newcommand\LenToUnit[1]{#1\@gobble}
220 | \newcommand\AtPageUpperLeft[1]{%
221 | \begingroup
222 | \@tempdima=0pt\relax\@tempdimb=\ESO@yoffsetI\relax
223 | \put(\LenToUnit{\@tempdima},\LenToUnit{\@tempdimb}){#1}%
224 | \endgroup
225 | }
226 | \newcommand\AtPageLowerLeft[1]{\AtPageUpperLeft{%
227 | \put(0,\LenToUnit{-\paperheight}){#1}}}
228 | \newcommand\AtPageCenter[1]{\AtPageUpperLeft{%
229 | \put(\LenToUnit{.5\paperwidth},\LenToUnit{-.5\paperheight}){#1}}}
230 | \newcommand\AtPageLowerCenter[1]{\AtPageUpperLeft{%
231 | \put(\LenToUnit{.5\paperwidth},\LenToUnit{-\paperheight}){#1}}}%
232 | \newcommand\AtPageLowishCenter[1]{\AtPageUpperLeft{%
233 | \put(\LenToUnit{.5\paperwidth},\LenToUnit{-.96\paperheight}){#1}}}
234 | \newcommand\AtTextUpperLeft[1]{%
235 | \begingroup
236 | \setlength\@tempdima{1in}%
237 | \advance\@tempdima\oddsidemargin%
238 | \@tempdimb=\ESO@yoffsetI\relax\advance\@tempdimb-1in\relax%
239 | \advance\@tempdimb-\topmargin%
240 | \advance\@tempdimb-\headheight\advance\@tempdimb-\headsep%
241 | \put(\LenToUnit{\@tempdima},\LenToUnit{\@tempdimb}){#1}%
242 | \endgroup
243 | }
244 | \newcommand\AtTextLowerLeft[1]{\AtTextUpperLeft{%
245 | \put(0,\LenToUnit{-\textheight}){#1}}}
246 | \newcommand\AtTextCenter[1]{\AtTextUpperLeft{%
247 | \put(\LenToUnit{.5\textwidth},\LenToUnit{-.5\textheight}){#1}}}
248 | \newcommand{\ESO@HookI}{} \newcommand{\ESO@HookII}{}
249 | \newcommand{\ESO@HookIII}{}
250 | \newcommand{\AddToShipoutPicture}{%
251 | \@ifstar{\g@addto@macro\ESO@HookII}{\g@addto@macro\ESO@HookI}}
252 | \newcommand{\ClearShipoutPicture}{\global\let\ESO@HookI\@empty}
253 | \newcommand{\@ShipoutPicture}{%
254 | \bgroup
255 | \@tempswafalse%
256 | \ifx\ESO@HookI\@empty\else\@tempswatrue\fi%
257 | \ifx\ESO@HookII\@empty\else\@tempswatrue\fi%
258 | \ifx\ESO@HookIII\@empty\else\@tempswatrue\fi%
259 | \if@tempswa%
260 | \@tempdima=1in\@tempdimb=-\@tempdima%
261 | \advance\@tempdimb\ESO@yoffsetI%
262 | \unitlength=1pt%
263 | \global\setbox\@cclv\vbox{%
264 | \vbox{\let\protect\relax
265 | \pictur@(0,0)(\strip@pt\@tempdima,\strip@pt\@tempdimb)%
266 | \ESO@HookIII\ESO@HookI\ESO@HookII%
267 | \global\let\ESO@HookII\@empty%
268 | \endpicture}%
269 | \nointerlineskip%
270 | \box\@cclv}%
271 | \fi
272 | \egroup
273 | }
274 | \EveryShipoutACL{\@ShipoutPicture}
275 | \newif\ifESO@dvips\ESO@dvipsfalse
276 | \newif\ifESO@grid\ESO@gridfalse
277 | \newif\ifESO@texcoord\ESO@texcoordfalse
278 | \newcommand*\ESO@griddelta{}\newcommand*\ESO@griddeltaY{}
279 | \newcommand*\ESO@gridDelta{}\newcommand*\ESO@gridDeltaY{}
280 | \newcommand*\ESO@yoffsetI{}\newcommand*\ESO@yoffsetII{}
281 | \ifESO@texcoord
282 | \def\ESO@yoffsetI{0pt}\def\ESO@yoffsetII{-\paperheight}
283 | \edef\ESO@griddeltaY{-\ESO@griddelta}\edef\ESO@gridDeltaY{-\ESO@gridDelta}
284 | \else
285 | \def\ESO@yoffsetI{\paperheight}\def\ESO@yoffsetII{0pt}
286 | \edef\ESO@griddeltaY{\ESO@griddelta}\edef\ESO@gridDeltaY{\ESO@gridDelta}
287 | \fi
288 |
289 |
290 | %% ----- Submitted version markup: Page numbers, ruler, and confidentiality. Using ideas/code from cvpr.sty 2015. --MM
291 |
292 | \font\naaclhv = phvb at 8pt
293 |
294 | %% Define vruler %%
295 |
296 | %\makeatletter
297 | \newbox\aclrulerbox
298 | \newcount\aclrulercount
299 | \newdimen\aclruleroffset
300 | \newdimen\cv@lineheight
301 | \newdimen\cv@boxheight
302 | \newbox\cv@tmpbox
303 | \newcount\cv@refno
304 | \newcount\cv@tot
305 | % NUMBER with left flushed zeros \fillzeros[]
306 | \newcount\cv@tmpc@ \newcount\cv@tmpc
307 | \def\fillzeros[#1]#2{\cv@tmpc@=#2\relax\ifnum\cv@tmpc@<0\cv@tmpc@=-\cv@tmpc@\fi
308 | \cv@tmpc=1 %
309 | \loop\ifnum\cv@tmpc@<10 \else \divide\cv@tmpc@ by 10 \advance\cv@tmpc by 1 \fi
310 | \ifnum\cv@tmpc@=10\relax\cv@tmpc@=11\relax\fi \ifnum\cv@tmpc@>10 \repeat
311 | \ifnum#2<0\advance\cv@tmpc1\relax-\fi
312 | \loop\ifnum\cv@tmpc<#1\relax0\advance\cv@tmpc1\relax\fi \ifnum\cv@tmpc<#1 \repeat
313 | \cv@tmpc@=#2\relax\ifnum\cv@tmpc@<0\cv@tmpc@=-\cv@tmpc@\fi \relax\the\cv@tmpc@}%
314 | % \makevruler[][][][][]
315 | \def\makevruler[#1][#2][#3][#4][#5]{\begingroup\offinterlineskip
316 | \textheight=#5\vbadness=10000\vfuzz=120ex\overfullrule=0pt%
317 | \global\setbox\aclrulerbox=\vbox to \textheight{%
318 | {\parskip=0pt\hfuzz=150em\cv@boxheight=\textheight
319 | \cv@lineheight=#1\global\aclrulercount=#2%
320 | \cv@tot\cv@boxheight\divide\cv@tot\cv@lineheight\advance\cv@tot2%
321 | \cv@refno1\vskip-\cv@lineheight\vskip1ex%
322 | \loop\setbox\cv@tmpbox=\hbox to0cm{{\naaclhv\hfil\fillzeros[#4]\aclrulercount}}%
323 | \ht\cv@tmpbox\cv@lineheight\dp\cv@tmpbox0pt\box\cv@tmpbox\break
324 | \advance\cv@refno1\global\advance\aclrulercount#3\relax
325 | \ifnum\cv@refno<\cv@tot\repeat}}\endgroup}%
326 | %\makeatother
327 |
328 |
329 | \def\aclpaperid{***}
330 | \def\confidential{ACL 2017 Submission~\aclpaperid. Confidential Review Copy. DO NOT DISTRIBUTE.}
331 |
332 | %% Page numbering, Vruler and Confidentiality %%
333 | % \makevruler[][][][][]
334 | \def\aclruler#1{\makevruler[14.17pt][#1][1][3][\textheight]\usebox{\aclrulerbox}}
335 | \def\leftoffset{-2.1cm} %original: -45pt
336 | \def\rightoffset{17.5cm} %original: 500pt
337 | \ifaclfinal\else\pagenumbering{arabic}
338 | \AddToShipoutPicture{%
339 | \ifaclfinal\else
340 | \AtPageLowishCenter{\thepage}
341 | \aclruleroffset=\textheight
342 | \advance\aclruleroffset4pt
343 | \AtTextUpperLeft{%
344 | \put(\LenToUnit{\leftoffset},\LenToUnit{-\aclruleroffset}){%left ruler
345 | \aclruler{\aclrulercount}}
346 | \put(\LenToUnit{\rightoffset},\LenToUnit{-\aclruleroffset}){%right ruler
347 | \aclruler{\aclrulercount}}
348 | }
349 | \AtTextUpperLeft{%confidential
350 | \put(0,\LenToUnit{1cm}){\parbox{\textwidth}{\centering\naaclhv\confidential}}
351 | }
352 | \fi
353 | }
354 |
355 | %%%% ----- End settings for placing additional items into the submitted version --MM ----- %%%%
356 |
357 | %%%% ----- Begin settings for both submitted and camera-ready version ----- %%%%
358 |
359 | %% Title and Authors %%
360 |
361 | \newcommand\outauthor{
362 | \begin{tabular}[t]{c}
363 | \ifaclfinal
364 | \bf\@author
365 | \else
366 | % Avoiding common accidental de-anonymization issue. --MM
367 | \bf Anonymous ACL submission
368 | \fi
369 | \end{tabular}}
370 |
371 | % Changing the expanded titlebox for submissions to 2.5 in (rather than 6.5cm)
372 | % and moving it to the style sheet, rather than within the example tex file. --MM
373 | \ifaclfinal
374 | \else
375 | \addtolength\titlebox{.25in}
376 | \fi
377 | % Mostly taken from deproc.
378 | \def\maketitle{\par
379 | \begingroup
380 | \def\thefootnote{\fnsymbol{footnote}}
381 | \def\@makefnmark{\hbox to 0pt{$^{\@thefnmark}$\hss}}
382 | \twocolumn[\@maketitle] \@thanks
383 | \endgroup
384 | \setcounter{footnote}{0}
385 | \let\maketitle\relax \let\@maketitle\relax
386 | \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\let\thanks\relax}
387 | \def\@maketitle{\vbox to \titlebox{\hsize\textwidth
388 | \linewidth\hsize \vskip 0.125in minus 0.125in \centering
389 | {\Large\bf \@title \par} \vskip 0.2in plus 1fil minus 0.1in
390 | {\def\and{\unskip\enspace{\rm and}\enspace}%
391 | \def\And{\end{tabular}\hss \egroup \hskip 1in plus 2fil
392 | \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\bf}%
393 | \def\AND{\end{tabular}\hss\egroup \hfil\hfil\egroup
394 | \vskip 0.25in plus 1fil minus 0.125in
395 | \hbox to \linewidth\bgroup\large \hfil\hfil
396 | \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\bf}
397 | \hbox to \linewidth\bgroup\large \hfil\hfil
398 | \hbox to 0pt\bgroup\hss
399 | \outauthor
400 | \hss\egroup
401 | \hfil\hfil\egroup}
402 | \vskip 0.3in plus 2fil minus 0.1in
403 | }}
404 |
405 | % margins for abstract
406 | \renewenvironment{abstract}%
407 | {\centerline{\large\bf Abstract}%
408 | \begin{list}{}%
409 | {\setlength{\rightmargin}{0.6cm}%
410 | \setlength{\leftmargin}{0.6cm}}%
411 | \item[]\ignorespaces}%
412 | {\unskip\end{list}}
413 |
414 | %\renewenvironment{abstract}{\centerline{\large\bf
415 | % Abstract}\vspace{0.5ex}\begin{quote}}{\par\end{quote}\vskip 1ex}
416 |
417 | \RequirePackage{natbib}
418 | % for citation commands in the .tex, authors can use:
419 | % \citep, \citet, and \citeyearpar for compatibility with natbib, or
420 | % \cite, \newcite, and \shortcite for compatibility with older ACL .sty files
421 | \renewcommand\cite{\citep} % to get "(Author Year)" with natbib
422 | \newcommand\shortcite{\citeyearpar}% to get "(Year)" with natbib
423 | \newcommand\newcite{\citet} % to get "Author (Year)" with natbib
424 |
425 |
426 | % bibliography
427 |
428 | \def\@up#1{\raise.2ex\hbox{#1}}
429 |
430 | % Don't put a label in the bibliography at all. Just use the unlabeled format
431 | % instead.
432 | \def\thebibliography#1{\vskip\parskip%
433 | \vskip\baselineskip%
434 | \def\baselinestretch{1}%
435 | \ifx\@currsize\normalsize\@normalsize\else\@currsize\fi%
436 | \vskip-\parskip%
437 | \vskip-\baselineskip%
438 | \section*{References\@mkboth
439 | {References}{References}}\list
440 | {}{\setlength{\labelwidth}{0pt}\setlength{\leftmargin}{\parindent}
441 | \setlength{\itemindent}{-\parindent}}
442 | \def\newblock{\hskip .11em plus .33em minus -.07em}
443 | \sloppy\clubpenalty4000\widowpenalty4000
444 | \sfcode`\.=1000\relax}
445 | \let\endthebibliography=\endlist
446 |
447 |
448 | % Allow for a bibliography of sources of attested examples
449 | \def\thesourcebibliography#1{\vskip\parskip%
450 | \vskip\baselineskip%
451 | \def\baselinestretch{1}%
452 | \ifx\@currsize\normalsize\@normalsize\else\@currsize\fi%
453 | \vskip-\parskip%
454 | \vskip-\baselineskip%
455 | \section*{Sources of Attested Examples\@mkboth
456 | {Sources of Attested Examples}{Sources of Attested Examples}}\list
457 | {}{\setlength{\labelwidth}{0pt}\setlength{\leftmargin}{\parindent}
458 | \setlength{\itemindent}{-\parindent}}
459 | \def\newblock{\hskip .11em plus .33em minus -.07em}
460 | \sloppy\clubpenalty4000\widowpenalty4000
461 | \sfcode`\.=1000\relax}
462 | \let\endthesourcebibliography=\endlist
463 |
464 | % sections with less space
465 | \def\section{\@startsection {section}{1}{\z@}{-2.0ex plus
466 | -0.5ex minus -.2ex}{1.5ex plus 0.3ex minus .2ex}{\large\bf\raggedright}}
467 | \def\subsection{\@startsection{subsection}{2}{\z@}{-1.8ex plus
468 | -0.5ex minus -.2ex}{0.8ex plus .2ex}{\normalsize\bf\raggedright}}
469 | %% changed by KO to - values to get teh initial parindent right
470 | \def\subsubsection{\@startsection{subsubsection}{3}{\z@}{-1.5ex plus
471 | -0.5ex minus -.2ex}{0.5ex plus .2ex}{\normalsize\bf\raggedright}}
472 | \def\paragraph{\@startsection{paragraph}{4}{\z@}{1.5ex plus
473 | 0.5ex minus .2ex}{-1em}{\normalsize\bf}}
474 | \def\subparagraph{\@startsection{subparagraph}{5}{\parindent}{1.5ex plus
475 | 0.5ex minus .2ex}{-1em}{\normalsize\bf}}
476 |
477 | % Footnotes
478 | \footnotesep 6.65pt %
479 | \skip\footins 9pt plus 4pt minus 2pt
480 | \def\footnoterule{\kern-3pt \hrule width 5pc \kern 2.6pt }
481 | \setcounter{footnote}{0}
482 |
483 | % Lists and paragraphs
484 | \parindent 1em
485 | \topsep 4pt plus 1pt minus 2pt
486 | \partopsep 1pt plus 0.5pt minus 0.5pt
487 | \itemsep 2pt plus 1pt minus 0.5pt
488 | \parsep 2pt plus 1pt minus 0.5pt
489 |
490 | \leftmargin 2em \leftmargini\leftmargin \leftmarginii 2em
491 | \leftmarginiii 1.5em \leftmarginiv 1.0em \leftmarginv .5em \leftmarginvi .5em
492 | \labelwidth\leftmargini\advance\labelwidth-\labelsep \labelsep 5pt
493 |
494 | \def\@listi{\leftmargin\leftmargini}
495 | \def\@listii{\leftmargin\leftmarginii
496 | \labelwidth\leftmarginii\advance\labelwidth-\labelsep
497 | \topsep 2pt plus 1pt minus 0.5pt
498 | \parsep 1pt plus 0.5pt minus 0.5pt
499 | \itemsep \parsep}
500 | \def\@listiii{\leftmargin\leftmarginiii
501 | \labelwidth\leftmarginiii\advance\labelwidth-\labelsep
502 | \topsep 1pt plus 0.5pt minus 0.5pt
503 | \parsep \z@ \partopsep 0.5pt plus 0pt minus 0.5pt
504 | \itemsep \topsep}
505 | \def\@listiv{\leftmargin\leftmarginiv
506 | \labelwidth\leftmarginiv\advance\labelwidth-\labelsep}
507 | \def\@listv{\leftmargin\leftmarginv
508 | \labelwidth\leftmarginv\advance\labelwidth-\labelsep}
509 | \def\@listvi{\leftmargin\leftmarginvi
510 | \labelwidth\leftmarginvi\advance\labelwidth-\labelsep}
511 |
512 | \abovedisplayskip 7pt plus2pt minus5pt%
513 | \belowdisplayskip \abovedisplayskip
514 | \abovedisplayshortskip 0pt plus3pt%
515 | \belowdisplayshortskip 4pt plus3pt minus3pt%
516 |
517 | % Less leading in most fonts (due to the narrow columns)
518 | % The choices were between 1-pt and 1.5-pt leading
519 | \def\@normalsize{\@setsize\normalsize{11pt}\xpt\@xpt}
520 | \def\small{\@setsize\small{10pt}\ixpt\@ixpt}
521 | \def\footnotesize{\@setsize\footnotesize{10pt}\ixpt\@ixpt}
522 | \def\scriptsize{\@setsize\scriptsize{8pt}\viipt\@viipt}
523 | \def\tiny{\@setsize\tiny{7pt}\vipt\@vipt}
524 | \def\large{\@setsize\large{14pt}\xiipt\@xiipt}
525 | \def\Large{\@setsize\Large{16pt}\xivpt\@xivpt}
526 | \def\LARGE{\@setsize\LARGE{20pt}\xviipt\@xviipt}
527 | \def\huge{\@setsize\huge{23pt}\xxpt\@xxpt}
528 | \def\Huge{\@setsize\Huge{28pt}\xxvpt\@xxvpt}
529 |
--------------------------------------------------------------------------------
/report/report.tex:
--------------------------------------------------------------------------------
1 | %
2 | % File acl2017.tex
3 | %
4 | %% Based on the style files for ACL-2015, with some improvements
5 | %% taken from the NAACL-2016 style
6 | %% Based on the style files for ACL-2014, which were, in turn,
7 | %% based on ACL-2013, ACL-2012, ACL-2011, ACL-2010, ACL-IJCNLP-2009,
8 | %% EACL-2009, IJCNLP-2008...
9 | %% Based on the style files for EACL 2006 by
10 | %%e.agirre@ehu.es or Sergi.Balari@uab.es
11 | %% and that of ACL 08 by Joakim Nivre and Noah Smith
12 |
13 | \documentclass[12pt,a4paper]{article}
14 | \usepackage[hyperref]{acl2017}
15 | \usepackage{times}
16 | \usepackage{latexsym}
17 | \usepackage{amsmath}
18 | \usepackage{url}
19 | \usepackage{graphicx}
20 | \usepackage{amssymb}
21 | \usepackage[noend]{algpseudocode}
22 | \usepackage{algorithmicx,algorithm}
23 | \usepackage{float}
24 |
25 | \newenvironment{figurehere} {\def\@captype{figure}} {}
26 |
27 | \aclfinalcopy % Uncomment this line for the final submission
28 | %\def\aclpaperid{***} % Enter the acl Paper ID here
29 |
30 | %\setlength\titlebox{5cm}
31 | % You can expand the titlebox if you need extra space
32 | % to show all the authors. Please do not make the titlebox
33 | % smaller than 5cm (the original size); we will check this
34 | % in the camera-ready version and ask you to change it back.
35 |
36 | \newcommand\BibTeX{B{\sc ib}\TeX}
37 |
38 | \title{Final Project: Smart Gomoku Agent}
39 |
40 | \author{Tianxiao Hu \\
41 | School of Computer Science\\
42 | Fudan University\\
43 | {\tt txhu14}\\{\tt @fudan.edu.cn} \\\And
44 | Hui Xu \\
45 | School of Data Science\\
46 | Fudan University\\
47 | {\tt xuhui14}\\{\tt @fudan.edu.cn} \\\And
48 | Bing Zhang \\
49 | School of Data Science\\
50 | Fudan University\\
51 | {\tt bingzhang14}\\{\tt @fudan.edu.cn}
52 | }
53 |
54 | \date{\today}
55 |
56 | \begin{document}
57 | \maketitle
58 | \begin{abstract}
59 | Our Gomoku game supports two types of games, including HUMAN VS AI and AI VS AI. A well-designed user interface is provided and 3 versions of AI algorithms are implemented: \textbf{Greedy}, \textbf{Minimax Search} and \textbf{Monte Carlo Tree Search}. For source code used in the project, please visit our code respository\footnote{https://github.com/TianxiaoHu/GomokuAgent}.
60 | \end{abstract}
61 |
62 | \section{Introduction}
63 | Gomoku, also called ``five in a row'', is a board game which originates from Japan. It is a game played on a Go board typically of size 15 x 15. In the game, players will take turns placing pieces until a player has managed to connect 5 in a row.
64 |
65 | \begin{figure}[!h]
66 | \centering\includegraphics[width=2.5in]{2.png}
67 | \caption{A Typical Gomoku Game}
68 | \end{figure}
69 |
70 | This project is aimed to develop a smart agent for Gomoku game. We use 3 main strategies to implement the agent: \textbf{Greedy}, \textbf{Minimax Search} and \textbf{Monte Carlo Tree Search(MCTS)}. We will introduce them in details in the following sections.\\
71 | We also developed a user interface for Gomoku game. Special thanks to open source project gobang\footnote{https://github.com/lihongxun945/gobang} for the beautiful design of the web page!
72 |
73 | \section{Evaluation Function}
74 | First of all, in order to quantify the properties of the current situation, we construct an evaluation function to estimate the win-probability. And naturally, the evaluation function will incorporate a great deal of the knowledge about the Gomoku game. Therefore, the evaluation can be either naive or complicated, which depends on the your understanding towards the Gomoku game. In general, the more complex the evaluation is, the slower the program will get over time.
75 |
76 | In this section, we mainly propose two versions of the evaluation function.
77 |
78 | \subsection{The First Version}
79 | The general idea of the first version is fairly simple. As is known to all, the object of the game is to be the first player to achieve five pieces in a row, horizontally, vertically or diagonally. Therefore, we can focus only on the five continuous positions on the chess board, hereinafter called ``five-tuple''. Generally, the chess board is $15\times 15$, having 572 five-tuples in all. Then we can assign a score to every five-tuple based on the number of the black and white pieces in it. Here we ignore the relative position of pieces. Then the evaluation to a given situation is the sum of the score of all 572 five-tuples. Suppose we take piece "x" and opponent takes piece "o", then the score table for me is displayed as follow,
80 | \begin{table}[h]
81 | \centering
82 | \begin{tabular}{c|c}
83 | \hline
84 | Five-Tuple&Score \\
85 | \hline
86 | x&15\\
87 | xx&400\\
88 | xxx&1800\\
89 | xxxx&100000\\
90 | xxxxx&10000000\\
91 | o&-35\\
92 | oo&-800\\
93 | ooo&-15000\\
94 | oooo&-800000\\
95 | ooooo&-10000000\\
96 | Blank&7\\
97 | Polluted&0\\
98 | \hline
99 | \end{tabular}
100 | \end{table}
101 |
102 | \noindent\begin{small}\emph{Note the score to blank five-tuple is 7 rather than 0. This is because there is worse condition, where the five-tuple is polluted, i.e., both black and white pieces in the tuple.}\end{small}
103 |
104 | The first version of the evaluation function is fairly simple and works rapidly. Nevertheless, after trying dozens of man-machine games, we find the agent can make some stupid mistakes, which result from its excessively simple evaluation function. Actually, the defect can be well solved by applying Minimax algorithm, but at a high price of the computing resource.
105 | \subsection{The Second Version}
106 | Then we intend to add more knowledge to the evaluation function to make it ``smarter''.
107 |
108 | In the second version of the evaluation function, we take more account of chess-types, or in other words, the relative position of the pieces.
109 |
110 | For every non-blank position on the chess board, we take it as the center and extend four positions to both sides horizontally, vertically and diagonally. Then we can obtain four nine-tuples. For each nine-tuple, we check its chess-type and assign a score according to the new score table. Add up these four scores and we can get the score of this position.
111 |
112 | Finally, our score is the sum of all positions occupied by our pieces while the opponent's score is the sum of all positions occupied by his pieces. And the evaluation to current situation is the difference of our score and opponent's score.
113 |
114 | The new score table is stated below,
115 | \begin{table}[h]
116 | \centering
117 | \begin{tabular}{c|c|c}
118 | \hline
119 | Chess-Type&Self-Score&Opp-Score \\
120 | \hline
121 | Five&1000000&1000000\\
122 | Alive-Four&20000&100000\\
123 | CoDash-Four&6100&65000\\
124 | GapDash-Four&6000&65000\\
125 | CoAlive-Three&1100&5500\\
126 | GapAlive-Three&1000&5000\\
127 | CoAsleep-Three&300&200\\
128 | GapAsleep-Three&290&200\\
129 | TeAsleep-Three&290&200\\
130 | FalseAlive-Three&290&200\\
131 | Alive-Two&100&90\\
132 | Asleep-Two&10&9\\
133 | One&3&4\\
134 | NoThreat&1&1\\
135 | \hline
136 | \end{tabular}
137 | \end{table}
138 |
139 | \noindent\begin{small}\emph{Note the names of the chess-types are fabricated by ourselves because we can't find accurate translation to these terms. If you are interest in the exact chess mode corresponding to the chess-types, please refer to the code or contact us.}\end{small}
140 |
141 | \subsection{Using Genetic Algorithm to find Better Evaluation Function}
142 | You may find in the score table above, the chess-types and corresponding self-scores and opponent scores are quite subjective. Thus, we apply genetic algorithm to find a better evaluation function, which is inspired by the process of natural selection\cite{ml}.
143 |
144 | We initialize the algorithm by setting the original score table as the very first generation. Afterwards, it can breed a new generation by randomly plus or minus 3\% specific chess-type score. The parent agent will play with each child agent 100 times and find the best children as the new parent. When the new generation can't defeat their parent, the algorithm comes to an end.
145 |
146 | The results are as follow:
147 | \begin{table}[h]
148 | \centering
149 | \begin{tabular}{c|c|c}
150 | \hline
151 | Chess-Type&Self-Score&Opp-Score \\
152 | \hline
153 | Five&1229874&1304773\\
154 | Alive-Four&27685&119405\\
155 | CoDash-Four&8198&84810\\
156 | GapDash-Four&7164&75353\\
157 | CoAlive-Three&1202&7176\\
158 | GapAlive-Three&1426&6720\\
159 | CoAsleep-Three&415&219\\
160 | GapAsleep-Three&378&239\\
161 | TeAsleep-Three&336&253\\
162 | FalseAlive-Three&357&246\\
163 | Alive-Two&127&111\\
164 | Asleep-Two&11&12\\
165 | One&4&5\\
166 | NoThreat&1&1\\
167 | \hline
168 | \end{tabular}
169 | \end{table}
170 |
171 |
172 | \section{Greedy Algorithm}
173 | Greedy algorithm is an algorithmic paradigm that follows the problem solving heuristic of making the locally optimal choice at each stage, with the hope of finding a global optimum.
174 |
175 | For our Gomoku agent, the evaluation function is exactly the so-called problem solving heuristic.
176 | And the implementation of the greedy search is elaborated as follows:
177 | \begin{algorithm}[h]
178 | \caption{Greedy Search}
179 | \hspace*{0.02in} {\bf Input:}
180 | chess board\\
181 | \hspace*{0.02in} {\bf Output:}
182 | position of move
183 | \begin{algorithmic}
184 | \For{each position (i, j) on the chess board}
185 | 閵嗏偓閵嗏偓\If{position (i, j) is blank}
186 | 閵嗏偓閵嗏偓閵嗏偓閵嗏偓\State suppose place my piece on (i, j)
187 | \State calculate my score: myScore
188 | \State calculate opponent score: opScore
189 | \State score(i, j) = myScore - opScore
190 | \EndIf
191 | \EndFor
192 | \Return the position with the max score
193 | \end{algorithmic}
194 | \end{algorithm}
195 |
196 | However, after dozens of games, we find the greedy strategy does not in general produce an optimal solution. On the one hand, owing to the limitation of the evaluation function, the evaluated score of the current situation can't describe the win-probability precisely. On the other hand, this is also the inherent defect of the greedy algorithm. But nonetheless a greedy heuristic may yield locally optimal solutions that approximate a global optimal solution in a reasonable time, which is appealing when solving many other problems.
197 |
198 |
199 | \section{Learn Openings from Game Record}
200 | After testing our agents for a number of games, we found opening is fairly important for Gomoku. If our agent is playing with a experienced human, it will easily lose the advantage during the opening. However, there are hundreds of different openings and they are hard to compute or collect. To solve the problem, we let our agent learn from 5581 top-level game records\footnote{http://game.onegreen.net/Soft/HTML/47233.html}. If current game matches some sequences of initial moves in the game records, it will compute scores for records and find the next step having the highest score.
201 |
202 | \section{Minimax Algorithm}
203 |
204 | \subsection{Introduction to Minimax and Alpha-Beta Prunning}
205 |
206 | In general games, the maximin value of a player is the largest value that the player can be sure to get without knowing the actions of the other players; equivalently, it is the smallest value the other players can force the player to receive when they know his action\cite{ai}.
207 |
208 | Calculating the maximin value of a player is done in a worst-case approach: for each possible action of the player, we check all possible actions of the other players and determine the worst possible combination of actions - the one that gives player $i$ the smallest value. Then, we determine which action player $i$ can take in order to make sure that this smallest value is the largest possible.
209 |
210 | \begin{figure}[H]
211 | \centering\includegraphics[width=2in]{3.png}
212 | \caption{Example: Minimax Search}
213 | \end{figure}
214 |
215 | In Gomoku game, we use \textbf{evaluation function} to score each chess board. After a player has placed a piece on the chess board, a new state is created right after the old. Afterwards, agents can search the tree and return the best choice. However, due to the large search space of Gomoku, the tree will expand rapidly and lead to a unbearable waiting time. We employed Alpha-Beta Prunning to accelerate our agent.
216 |
217 | Alpha-Beta pruning is a search algorithm that seeks to decrease the number of nodes that are evaluated by the Minimax algorithm in its search tree. It stops completely evaluating a move when at least one possibility has been found that proves the move to be worse than a previously examined move. Such moves need not be evaluated further.
218 |
219 | \begin{figure}[!h]
220 | \centering\includegraphics[width=3.2in]{4.png}
221 | \caption{Example: Minimax Search}
222 | \end{figure}
223 |
224 | Our pseudocode is listed as right-above.
225 | \begin{small}
226 | \begin{algorithm}[!h]
227 | \caption{Alpha-Beta Pruning algirithm}
228 | \hspace*{0.02in} {\bf function}
229 | alphabeta(node, depth, $\alpha, \beta$, maximizingPlayer)
230 | \begin{algorithmic}
231 | \If{depth = 0 or node is a terminal node}
232 | \State \Return the heuristic value of node
233 | \If{maxmizingPlayer}
234 | \State v $\gets - \infty$
235 | \For{each child of node}
236 | \State v $\gets$ max(v, alphabeta(child, depth - 1, $\alpha$, $\beta$, FALSE)
237 | \State $\alpha \gets max(\alpha, v)$
238 | \If{$\beta \leq \alpha$}
239 | \State break
240 | \EndIf
241 | \State \Return v
242 | \EndFor
243 | \Else
244 | \State $v \gets + \infty$
245 | \For{each child of node}
246 | \State v $\gets$ min(v, alphabeta(child, depth - 1, $\alpha$, $\beta$, TRUE)
247 | \State $\beta \gets max(\beta, v)$
248 | \If{$\beta \leq \alpha$}
249 | \State break
250 | \EndIf
251 | \State \Return v
252 | \EndFor
253 | \EndIf
254 | \EndIf
255 | \end{algorithmic}
256 | \end{algorithm}
257 | \end{small}
258 |
259 | \subsection{Speed Optimization}
260 | The depth of search tree is an important parameter in Minimax Search. If layers searched are not enough, our agent will be really short-sighted. However, for the sake of Python's efficiency, our naive implementation can only search for 2 layers in 1 second. If we force it to search 4 layers, the waiting time will become 10 seconds. Further optimization is needed.
261 |
262 | We use several methods to accelerate our program:
263 |
264 | \begin{itemize}
265 | \item \textbf{Less Search States}\\
266 | We explore less search state for each player. Instead of search all possible place for a piece, we only consider places near pieces which are already placed in the chess board.
267 | \item \textbf{Zobrist Hashing States in Memory}\\
268 | During the expanding of the search tree, some nodes may share the same state. Their scores will be the same so we need not calculate them twice. We save each state and its score in memory to accelerate the agent.\\
269 | However, a state is hard to perform hashing. We use Zobrist Hashing to represent states. Zobrist Hashing starts by randomly generating bitstrings for each possible element of a board game, i.e. for each combination of a piece and a position. Now any board configuration can be broken up into independent position components, which are mapped to the random bitstrings generated earlier. The final Zobrist Hashing is computed by combining those bitstrings using bitwise XOR. When updating positions, rather than computing the hash for the entire chess board every time, the hash value of a chess board can be updated simply by XOR out the bitstring for positions that have changed, and XOR in the bitstrings for the new positions.
270 | \item \textbf{Python's Numba Package}\\
271 | Our implementation is based on Python's scientific computing packages Numpy. We use Numba package to accelerate it. Numba is an Open Source NumPy-aware optimizing compiler for Python. It uses the LLVM compiler infrastructure to compile Python to machine code. This optimized function runs 200 times faster than the interpreted original function on a long NumPy array; and it is 30\% faster than NumPy's builtin \emph{sum()} function\footnote{https://en.wikipedia.org/wiki/Numba}.
272 |
273 | \end{itemize}
274 |
275 | Our optimized Alpha-Beta Prunning can search 8 layers within 1 second. And it turned out to be really hard to defeat especially when the agent goes first.
276 |
277 | \section{Monte Carlo Tree Search}
278 | \subsection{Introduction to MCTS and UCT}
279 | \par Monte Carlo Tree Search (MCTS) is a tree search technique expanding the search tree based on random sampling of the search space. The application of Monte Carlo tree search in games is based on many playouts. In each playout, the game is played out to the very end by selecting moves at random. The final game result of each playout is then used to weight the nodes in the game tree so that better nodes are more likely to be chosen in
280 | future playouts. The strategy is going to have to balance playing all of the machines to gather that information, with concentrating the plays on the observed best machine. One strategy, called UCB1, does this by constructing statistical confidence intervals for each machine.
281 |
282 | \begin{displaymath}
283 | \bar{x}_i \pm \sqrt{\frac{C\ln n}{n_i}}
284 | \end{displaymath}
285 | \begin{center}
286 | $\bar{x}_i$: the mean playout for machine \emph{i}\\
287 | $n_i$: the number of plays of machine \emph{i} \\
288 | $n$: the total number of plays
289 | \end{center}
290 |
291 | Then, the strategy is to pick the machine with the highest upper bound each time. Upper Confidence bound applied to Trees (UCT)
292 | is MCTS with UCB strategy.
293 | \par For Gomoku game, MCTS starts with a chess board and walk chess randomly until the end. The process is repeated many times which eliminates the best move for the current chess board. Each round of Monte Carlo tree search consists of four steps:\\
294 |
295 | \begin{figurehere}
296 | \centering
297 | \includegraphics[width=90mm]{1.png}
298 | \end{figurehere}
299 | \begin{itemize}
300 | \item Selection:
301 | Build the root node based on the current chess board and generate all of its child nodes. The move to use would be chosen by the UCB1 algorithm and applied to obtain the next position to be considered.
302 | \item Expansion:
303 | Selection would then proceed until reach a position where not all of the child positions have statistics recorded.
304 | \item Simulation:
305 | If the node hasn't been simulated, then do a typical Monte Carlo simulation for chess game. Else, generate a random child node for the leaf node and do the simulation.
306 | \item Backpropagation:
307 | Update the reward of the simulation (generally 0 for lose and 1 for win) to the leaf node and its ancestor node. Meanwhile add the number of view for every node in the search path.
308 | \end{itemize}
309 |
310 |
311 | %Example: Monte Carlo Tree Search
312 | \par Repeat the playouts for many times until reach the search time or max search times and we can get the best move by selecting the maximum reward child node for the current root board. The pseudocode is showed as follow.
313 | \begin{algorithm}[H]
314 | \caption{The UCT algorithm}
315 | \hspace*{0.02in} {\bf function}
316 | UCTSearch($s_0$)
317 | \begin{algorithmic}
318 | \State create root node $v_0$ with state $s_0$
319 | \While{within computational budget}
320 | \State $v_l \gets TreePolicy(v_0)$
321 | \State $\Delta \gets DefaultPolicy(s(v_l))$
322 | \State $BackUp(v_l, \Delta)$
323 | \EndWhile
324 | \State \Return $a(BestChild(v_0, 0))$
325 | \end{algorithmic}
326 | ~\\
327 | \hspace*{0.02in} {\bf function}
328 | TreePolicy(v)
329 | \begin{algorithmic}
330 | \While{v is nonterminal}
331 | \If{v not fully expanded}
332 | \State \Return Expand(v)
333 | \Else
334 | \State $v \gets BestChild(v, Cp)$
335 | \EndIf
336 | \EndWhile
337 | \State \Return v
338 | \end{algorithmic}
339 | ~\\
340 | \hspace*{0.02in} {\bf function}
341 | Expand(v)
342 | \begin{algorithmic}
343 | \State choose a $\in$ untried actions from A(s(v))
344 | \State add a new child v' to v with s(v') = f(s(v),a) and a(v') = a
345 | \State \Return v'
346 | \end{algorithmic}
347 | ~\\
348 | \hspace*{0.02in} {\bf function}
349 | BestChild(v,c)
350 | \begin{algorithmic}
351 | \State \Return $\mathop{\arg\max} \frac{Q(v')}{N(v')} + c\sqrt{\frac{2lnN(v)}{N(v')}}$
352 | \end{algorithmic}
353 | ~\\
354 | \hspace*{0.02in} {\bf function}
355 | DefaultPolicy(s)
356 | \begin{algorithmic}
357 | \While{s is non-terminal}
358 | \State choose a $\in$ A(s) uniformly at random
359 | \State s $\gets$ f(s,a)
360 | \EndWhile
361 | \State \Return reward for state s
362 | \end{algorithmic}
363 | ~\\
364 | \hspace*{0.02in} {\bf function}
365 | BackUp(v,$\Delta$)
366 | \begin{algorithmic}
367 | \While{v is not null}
368 | \State N(v) $\gets$ N(v) + 1
369 | \State Q(v) $\gets$ Q(v) + $\delta(v, p)$
370 | \State v $\gets$ parent of v
371 | \EndWhile
372 | \end{algorithmic}
373 | \end{algorithm}
374 | \par Obviously the tree structure for a 15x15 Gomoku game chess board is too large, so the machine needs too many simulations for each choice of move and it take a long time for AI's move. Thus, the game experience is not very well.
375 | \par Since we cannot speed up our algorithm by using cluster environment, we conduct a 4-in-row game in a 7x7 game chess board. And here is a view for a game result.
376 |
377 | \begin{figure}[H]
378 | \centering\includegraphics[width=3in]{ai3.png}
379 | \caption{Result for MCTS algorithm}
380 | \end{figure}
381 |
382 |
383 |
384 | \section{Round Robin for Agents}
385 | Several agents are implemented and we hold a round robin for them. From the competition result we can make a detailed analysis on different strategies.\\
386 | Information of agents is listed here:
387 | \begin{itemize}
388 | \item AI1: Greedy Algorithm with evaluation function version 1
389 | \item AI2: 2-layer Minimax Search with evaluation function version 1
390 | \item AI3: Monte Carlo Tree Search
391 | \item AI4: 8-layer Minimax Search with evaluation function version 1
392 | \item AI5: Greedy Algorithm with evaluation function version 2
393 | \item AI6 Greedy Algorithm with evaluation function version 1 but also known better openings
394 | \end{itemize}
395 | Note that AI3 runs quite slow and due to the large search space, it's performance is not satisfying. Besides, \textbf{Monte Carlo Tree Search} approach sometimes attempts to place a few pieces away from current pieces and build up ``combination moves'' that way, which is different from all other agents.
396 | \begin{itemize}
397 | \item \textbf{AI1 vs. AI2}\\
398 | The difference is between Greedy Algorithm and 2-layer Minimax Search. That's to say, whether the agent can consider the opponent's next step matters. We find 2-layer Minimax Search has a slight advantage. If AI2 goes first, it will win all the 100 games. But if AI1 goes first, things become quite different. AI1 can win 14 games and 6 games turn out to be tie. AI2 still has 80\% winning percentage regardless of advantage of the upper edge.
399 | \item \textbf{AI1 vs. AI5}\\
400 | The difference between the two is the evaluation function. Contrasted to AI1, the evaluation function of AI5 is more reasonable, considering more possible cases. If AI1 goes first, it will win 75\%games and if AI5 goes first, AI5 will win 89\% games. The competition result has relevance to the order for placing pieces. But in total, AI5 performs better.
401 | \item \textbf{AI2 vs. AI5}\\
402 | When AI2 encounters with AI5, only one more layer for searching seems to be of no use. AI5 beats AI2 in each game and the result is independent of which AI goes first.
403 | \item \textbf{AI1 vs. AI6}\\
404 | AI6 can learn next step from 5570 top-level game records. When AI6 goes first, it can beat AI1 easily in a short time. But when AI1 goes first, AI6 can only win 50\% games. The reason is that AI1 plays straight forward to carry out ``five in a row'' while AI6 will find a best place for latter pieces.
405 | \item \textbf{AI2 vs. AI6}\\
406 | Interestingly, AI6 has a very high winning percentage(90\%) against AI2. We guess the reason is top-level openings can provide more opportunities for latter pieces. The games also end quickly, all of which less than 30 pieces in total.
407 | \item \textbf{AI5 vs. AI6}\\
408 | After AI1 has learned better openings, it performs better in competitions with AI5. If AI6 goes first, it can win about 50\% games(AI1: 25\%). However, it nearly has nothing to do with the winning percent when AI5 goes first. The winning percent only rise from 11\% to 13\%.
409 | \item \textbf{AI4 vs. all other AI}\\
410 | The depth of searching tree begins to dominate in the competitions of AI4. AI4 can easily beat any other AIs, even when AI4 is not the one goes first. \\
411 | Although AI4's evaluation function is not the best, it can gain an advantage step by step. If other AI goes first, AI4 may not perform very well at the very beginning of the game(always be busy defending). However, after about 30 pieces are placed, the situation will become balanced. After about 50 pieces are placed, AI4 will win.\\
412 | By the way, we invited several students in Fudan University that are good at Gomoku game to test our agents. According to their feedback, AI4 plays the best among all AIs especially AI4 goes first. ``It's hard to defeat'', one of the players told us.
413 | \end{itemize}
414 |
415 |
416 | % include your own bib file like this:
417 | \bibliographystyle{acl_natbib}
418 | \bibliography{ref}
419 |
420 | \end{document}
421 |
--------------------------------------------------------------------------------