// ==UserScript==
// @name 2048bot
// @namespace http://tampermonkey.net/
// @version 0.9415
// @description Lie back and watch the dumb bot play!
// @author boynextdesk
// @match https://play2048.co/*
// @icon https://play2048.co/favicon.ico
// @grant none
// @license GNU General Public License v3.0
// ==/UserScript==
/*
* 128 64 32 16
* 64 32 16 8
* 32 16 8 4
* 16 8 4 2
*
* */
(function() {
// todo: inspect whether game is over
// todo: train strategy after game-over
// todo: set speed and strategy through page
// todo: beautify layout
// todo: everlasting game - auto retry until winning
// todo: bundle direction-ava functinos into an object to global users
'use strict';
const eventUp = new KeyboardEvent('keydown', {
key: "w",
keyCode: 87,
which: 87,
code: "KeyW",
location: 0,
description: "w"
});
const eventLeft = new KeyboardEvent('keydown', {
key: "a",
keyCode: 65,
which: 65,
code: "KeyA",
location: 0,
description: "a"
});
const eventRight = new KeyboardEvent('keydown', {
key: "d",
keyCode: 68,
which: 68,
code: "KeyD",
location: 0,
description: "d"
});
const eventDown = new KeyboardEvent('keydown', {
key: "s",
keyCode: 83,
which: 83,
code: "KeyS",
location: 0,
description: "s"
});
// thanks to https://zeit.co/blog/async-and-await
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function lose() {
return document.getElementsByClassName("game-message game-over")[0] != null
}
function win() {
return document.getElementsByClassName("game-message game-win")[0] != null
}
function over() {
return lose() || win()
}
function inspect() {
const board = Array();
for(let j = 1 ; j <= 4 ; j++) // row
{
const unit4 = Array();
for(let i = 1 ; i <= 4 ; i++) //column
{
const className = "tile-position-"+i+"-"+j
const tiles = document.getElementsByClassName(className)
const len = tiles.length
const tile = tiles[len - 1]
/// console.log(className)
let val = 0;
if(tile != null){
val = tile.firstChild.lastChild.nodeValue
val = Number(val)
/// console.log(val)
}
unit4[i] = val
}
board[j] = unit4
}
return board
}
const aboveBox = document.getElementsByClassName("above-game")[0];
const btnTemplate = document.getElementsByClassName("restart-button")[0];
let speed = 10;
let speedIndex = 0;
let switchU = true;
aboveBox.appendChild(btnTemplate.cloneNode(false));
aboveBox.appendChild(btnTemplate.cloneNode(false));
aboveBox.appendChild(btnTemplate.cloneNode(false));
//
const btns = document.getElementsByClassName("restart-button");
//
const btnStartAuto = btns[1];
const startText = document.createTextNode("Start Auto");
btnStartAuto.appendChild(startText)
//
const btnStopAuto = btns[2];
const stopText = document.createTextNode("Stop Auto");
btnStopAuto.appendChild(stopText)
//
const btnSpeedAuto = btns[3];
const speedText = document.createTextNode("Change Speed");
btnSpeedAuto.appendChild(speedText)
//
let moveOn = false;
let moveCnt = 0;
let globalBoard = null;
document.tagName = "not input"
const strategy01 = function () {
const num = Math.random();
let dispatchee = null;
moveCnt += 1
const rate = 1 - Math.min(48 / moveCnt, 0.8)
const downL = 0.02 * rate
const rightL = downL + 0.02 * rate;
if (num < downL) {
dispatchee = eventDown;
console.log("down");
} else if (num < rightL) {
dispatchee = eventRight;
console.log("right");
} else if (switchU) {
dispatchee = eventLeft
console.log("left")
switchU = false
} else {
dispatchee = eventUp
console.log("up")
switchU = true
}
return dispatchee
};
const strategy02 = function () {
const board = globalBoard
if(board == null)
return eventLeft
const directionAvailable = function(offsetI, offsetJ){
for(let i = 1 ; i <= 4 ; i++){
for(let j = 1 ; j <= 4 ; j++){
const ni = i + offsetI
const nj = j + offsetJ
if(ni < 1 || ni > 4) continue
if(nj < 1 || nj > 4) continue
if(board[i][j] === 0) continue;
if(board[ni][nj] === board[i][j] || board[ni][nj] === 0)
return true
}
}
return false
}
const dir = [[0, -1], [-1, 0], [1, 0], [0, 1]]
const msg = ["left", "up", "down", "right"]
const funcArr = [eventLeft, eventUp, eventDown, eventRight]
for(let i = 0 ; i < 4 ; i++){
const con = directionAvailable(dir[i][0], dir[i][1])
if(con){
console.log(msg[i])
return funcArr[i]
}
//console.log(msg[i] + " unavailable")
}
return funcArr[0]
}
const moveOneKey = function () {
if (!moveOn) {
console.log("Cancelled.")
return
}
const dispatchee = strategy02();
document.dispatchEvent(dispatchee)
};
const startListener = async function () {
console.log("start")
moveOn = true;
while (moveOn) {
await sleep(10 * speed).then(() => {
if(over()){
moveOn = false
console.log("Game over.")
}
const board = inspect();
console.log(board)
globalBoard = board
})
await sleep(70 * speed).then(moveOneKey)
}
};
const stopListener = function () {
moveOn = false;
moveCnt = 0
}
const speedListener = function () {
const speeds = [10, 5, 1];
const words = ["switch to low speed", "switch to fast speed", "switch to super fast speed"];
const len = 3
speedIndex = (speedIndex + 1) % len
speed = speeds[speedIndex]
console.log(words[speedIndex])
}
btnStartAuto.addEventListener('click', startListener)
btnStopAuto.addEventListener('click', stopListener)
btnSpeedAuto.addEventListener('click', speedListener)
})();