2048 game bot

Categories: howto

Есть такая игра 2048, суть ее в том чтобы собрать квадрат 2048 из других меньших квадратов, складываются квадраты только с одинаковой цифрой 2+2, 4+4, 8+8 и т.д

Игра написана на Javascript и HTML, а это значит можно попробовать написать бота. Открыв инспектор я сразу посмотрел на структуру этих квадратов, все оказалось очень просто об этом ниже под катом.

1
<div class="tile tile-8 tile-position-3-3"><div class="tile-inner">8</div></div>

как видно это это обычный div с классами tile-8 например означало что это “8”, чтобы распарсить все квадраты достаточно было сделать следующее:

1
var tiles = document.getElementsByClassName('tile');

После чего получив все элементы div с классом tile можно было легко с ними работать. Заморчиватся с умным алгоритмом нехотелось поэтому простой рандом из 4 типов ходов (влево, вверх, вправо, вниз) вполне подходил.

Чтобы реализовать эмуляцию нажатия кнопки в браузере нужно сделать следюющее

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function keyDown(evt)
{
    var key;
    if(!evt)
    {
        evt = window.event;
        if(!evt.which)
        {
            key  =  evt.keyCode;   
        }
    }
	else if(evt)
    {
        key = evt.which;   
    }	
}


function fireKey(el, key)
{
	
    if(document.createEventObject)
    {
        var eventObj = document.createEventObject();
        eventObj.keyCode = key;
        el.fireEvent("onkeydown", eventObj);   
    }
	else if(document.createEvent)
    {
        var eventObj = document.createEvent("Events");
        eventObj.initEvent("keydown", true, true);
        eventObj.which = key;
        el.dispatchEvent(eventObj);
    } 
}


fireKey(document.getElementByClassName('game-container')[0], 40);

Нужно создать обьект события нажатия кнопки, и присовать нужные свойства, в данном случае это keycode нужной кнопки. В параметрах которые передаются в метод fireKey разберетесь сами.

Еще один момент, когда просиходит проигрышь, надо перезапускать игру, для этого надо отслеживать специальный div который становится видимым, и после этого надо нажимать кнопку New Game. Для этого надо просто отследить его свойство display.

На этом собствтенно все, в коде заложена возможность сделать бота более интелектуальным, можете попробовать сами.

Github: https://github.com/noroot/2048-bot

Результат работы на видео:

Ну и для удобства код целиком здесь, чтобы его запустить надо скопировать, открыть игру и открыть консоль, и вставить туда этот код целиком, он сразу начнет работать.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
 * 2048 game bot
 * usage: startgame and paste code to browser development console
 */

function explode( delimiter, string ) {	// Split a string by string
	// 
	// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +   improved by: kenneth
	// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)

	var emptyArray = { 0: '' };

	if ( arguments.length != 2
		|| typeof arguments[0] == 'undefined'
		|| typeof arguments[1] == 'undefined' )
	{
		return null;
	}

	if ( delimiter === ''
		|| delimiter === false
		|| delimiter === null )
	{
		return false;
	}

	if ( typeof delimiter == 'function'
		|| typeof delimiter == 'object'
		|| typeof string == 'function'
		|| typeof string == 'object' )
	{
		return emptyArray;
	}

	if ( delimiter === true ) {
		delimiter = '1';
	}

	return string.toString().split ( delimiter.toString() );
}

function keyDown(evt)
{
    var key;
    if(!evt)
    {
        evt = window.event;
        if(!evt.which)
        {
            key  =  evt.keyCode;   
        }
    }
	else if(evt)
    {
        key = evt.which;   
    }	
}


function fireKey(el, key)
{
	
    if(document.createEventObject)
    {
        var eventObj = document.createEventObject();
        eventObj.keyCode = key;
        el.fireEvent("onkeydown", eventObj);   
    }
	else if(document.createEvent)
    {
        var eventObj = document.createEvent("Events");
        eventObj.initEvent("keydown", true, true);
        eventObj.which = key;
        el.dispatchEvent(eventObj);
    } 
}


function logscore() 
{
	var tiles = [128, 256, 512, 1024, 2048];
	var arlen = tiles.length;
	for(var i = 0; i <= arlen;i++) 
	{
		score = tiles[i];
		if (document.getElementsByClassName("tile-" + score).length != 0)
		{
			console.log("MAX SCORE=" + score);
		}
		else
		{
			console.log("FAIL");
		}
	}
}

// define key codes

var left = 37;
var up = 38;
var right = 39;
var down = 40;

var clicks = 0;
var tries = 0;


document.documentElement.focus();
document.onkeydown = keyDown;


//var fired = 0;
var matrix_object = [];
var matrix = [];
var selected_turn = 0;
var	prefered_turn = 0;


var gamebox = document.getElementsByClassName("game-container")[0];


function calculate_turn()
{
	var fired = 0;
	
	var arr = document.getElementsByClassName('tile');
	selected_turn = 0;
	prefered_turn = 0;

	var turns = [];	
	matrix_object = [];

	// prepare object for more smart solving (not used in future)
	for(var i=0; i <= arr.length; i++)
	{
		var el = arr[i];
		if (el)
		{
			var t = explode("-", el.classList[1]);
			var tilescore = t[1];
			var t = explode("-", el.classList[2]);
			var tp = {'score': tilescore, 'x':t[2], 'y':t[3]};
			if (tp.x!=='undefined')
			{
				matrix_object.push(tp);
			}
		}
		
	}
	
	// TODO:
	// make clever work here
	

	// until clever work done just go random
	if (prefered_turn == 0 || selected_turn == 0)
	{
		var keys = [up,right,down]; // tricky turns, without left turn in case of win
		var key = keys[Math.floor(Math.random() * keys.length)];
		selected_turn = key;
	}
	return selected_turn;
}

function make_turn()
{
	
	if (document.getElementsByClassName("game-over").length > 0)
	{
		console.clear();
		logscore();
		tries++;
		console.log("Restart (" + tries + ", "+clicks+")");
		clicks = 0;
		document.getElementsByClassName("restart-button")[0].click();
		
	}
	else if (document.getElementsByClassName("game-won").length > 0)
	{
		clearInterval(interval);
		console.log("win");
	}
	else
	{
		clicks++;
		var turn = calculate_turn();
		fireKey(gamebox, turn);
		
	}
	
}

var interval = setInterval(make_turn, 500);
console.log(matrix_object);


No webmentions were found.

Comment with an Email

No comments here yet Write here gently