Skip to content

Commit

Permalink
remove excessive event listener creation
Browse files Browse the repository at this point in the history
  • Loading branch information
ziap committed Jan 22, 2024
1 parent b29bf9d commit 5f3b542
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 68 deletions.
88 changes: 46 additions & 42 deletions js/ai.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,68 @@
import { state, stateBuffer } from "./state.js";

function createWorker() {
const worker = new Worker('./js/worker.js')
function createWorkers(count) {
const workers_promise = new Array(count)
for (let i = 0; i < count; ++i) {
const worker = new Worker('./js/worker.js')

return new Promise(resolve => worker.addEventListener('message', e => {
const msg = e.data
if (msg != 'ready') throw new Error(`Expected 'ready', got ${msg}`)
workers_promise[i] = new Promise(resolve => {
worker.addEventListener('message', e => {
const msg = e.data
if (msg != 'ready') throw new Error(`Expected 'ready', got ${msg}`)

resolve(worker)
}, { once: true }))
resolve(worker)
}, { once: true })
})
}

return Promise.all(workers_promise)
}

function createWorkers(count) {
let workers = [createWorker()]
for (let i = 1; i < count; ++i) workers.push(createWorker())
const workerCount = Math.max(navigator.hardwareConcurrency, 1)

return Promise.all(workers)
}
export async function registerAI(callback) {
let workers = await createWorkers(workerCount)
let working = 0

const workerCount = navigator.hardwareConcurrency
function workerCallback(e) {
if (working == workers.length) state.set_children(e.data)
else state.add_children(e.data)

let workers = await createWorkers(workerCount)
let working = false
working--

export async function invokeAI(strength) {
const start = performance.now()
let promises = []
if (!working) {
state.best_move(e.data.byteLength)
callback()
}
}

for (const worker of workers) {
promises.push(new Promise(resolve => {
worker.addEventListener('message', e => {
resolve(e.data)
}, { once: true })
}))
worker.addEventListener('message', workerCallback)
}

working = true
for (const worker of workers) {
const arr = new Uint8Array(stateBuffer.length)
arr.set(stateBuffer)
function invokeAI(strength) {
working = workers.length
for (const worker of workers) {
const arr = new Uint8Array(stateBuffer.length)
arr.set(stateBuffer)

const buf = arr.buffer
worker.postMessage({ strength, buf }, [buf])
const buf = arr.buffer
worker.postMessage({ strength, buf }, [buf])
}
}

const results = await Promise.all(promises)
working = false
async function stopAI() {
if (!working) return

state.set_children(results[0])
for (let i = 1; i < promises.length; ++i) state.add_children(promises[i])
for (const worker of workers) worker.terminate()
workers = await createWorkers(workerCount)

state.best_move(results[0].byteLength)
console.log(`Search time: ${performance.now() - start}`)
}

export async function stopAI() {
if (!working) return
for (const worker of workers) {
worker.addEventListener("message", workerCallback)
}

for (const worker of workers) worker.terminate()
workers = await createWorkers(workerCount)
working = 0
}

working = false
return { invokeAI, stopAI }
}
33 changes: 13 additions & 20 deletions js/game.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { invokeAI, stopAI } from './ai.js'
import { registerAI } from './ai.js'
import { state } from './state.js'

const gameRoot = document.querySelector('#container')

const mainGrid = document.querySelector('#main-grid')
let mainCells = []
let subCells = []
let subGrids = []
let mainCells = new Array(9)
let subGrids = new Array(9)
let subCells = new Array(9 * 9)

for (let i = 0; i < 9; ++i) {
const mainCell = document.createElement('div')
Expand All @@ -23,18 +23,17 @@ for (let i = 0; i < 9; ++i) {

state.move(i, j)
updateHTML()
AIMove()
})

subGrid.appendChild(subCell)
subCells.push(subCell)
subCells[9 * i + j] = subCell
}

mainCell.appendChild(subGrid)
mainGrid.appendChild(mainCell)

subGrids.push(subGrid)
mainCells.push(mainCell)
subGrids[i] = subGrid
mainCells[i] = mainCell
}

const settingsButton = gameRoot.querySelector('#settings')
Expand All @@ -44,6 +43,8 @@ const endMessage = gameRoot.querySelector('#end-message')
let playerTurn = [true, false]
let AIStrength = 32

const { invokeAI, stopAI } = await registerAI(updateHTML)

function updateHTML() {
for (const elem of mainCells) elem.className = 'main-cell'
for (const elem of subCells) elem.className = 'sub-cell'
Expand All @@ -60,7 +61,7 @@ function updateHTML() {
case 0: {
if (state.result) break
if (state.lastMove != -1 && state.lastMove != i) break
subGrids[i].classList.add('active');
subGrids[i].classList.add('active')
} break
case 1: mainCells[i].classList.add('x'); continue
case 2: mainCells[i].classList.add('o'); continue
Expand All @@ -78,25 +79,17 @@ function updateHTML() {
}
}
}
}

async function AIMove() {
if (state.result || playerTurn[state.currentPlayer]) return

await invokeAI(AIStrength)

updateHTML()

if (!playerTurn[state.currentPlayer]) AIMove()
if (!(state.result || playerTurn[state.currentPlayer])) {
invokeAI(AIStrength)
}
}

async function reset() {
await stopAI()

state.reset()
updateHTML()

AIMove()
}

restartButton.addEventListener('click', reset)
Expand Down
3 changes: 1 addition & 2 deletions js/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ function cstr(ptr) {
let len = 0;
while (mem_arr[len]) ++len

const bytes = mem_arr.slice(0, len)
return decoder.decode(bytes);
return decoder.decode(mem_arr.subarray(0, len))
}

const env = {
Expand Down
3 changes: 1 addition & 2 deletions js/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
let len = 0;
while (mem_arr[len]) ++len

const bytes = mem_arr.slice(0, len)
return decoder.decode(bytes);
return decoder.decode(mem_arr.subarray(0, len))
}

const env = {
Expand Down
5 changes: 3 additions & 2 deletions src/wasm/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ void* children_ptr() { return children; }

void combine_children(u32 children_size) {
usize count = children_size / sizeof(node_t);
node_t *other = children + count;

for (usize i = 0; i < count; ++i) {
children[i].value += children[i + count].value;
children[i].samples += children[i + count].samples;
children[i].value += other[i].value;
children[i].samples += other[i].samples;
}
}

Expand Down
Binary file modified wasm/ai.wasm
Binary file not shown.
Binary file modified wasm/state.wasm
Binary file not shown.

0 comments on commit 5f3b542

Please sign in to comment.