Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: joshua example #12

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions example/joshua/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Joshua

A tic-tac-toe with a web ui inspired by the movie "War Games"

## About

The UI is a webpage and is using a websocket to fulfill the encoder interface

## Screenshot

![](screenshot.gif)
14 changes: 14 additions & 0 deletions example/joshua/datatype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import "github.com/gorgonia/agogo/game"

const (
kindPlay = 0
kindInfo = 1
)

type info struct {
Epoch int
Game int
Winner game.Player
}
203 changes: 203 additions & 0 deletions example/joshua/htdocs/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
'use strict';

var player = 1;
var lineColor = "#7D96BF";

var canvas = document.getElementById('tic-tac-toe-board');
var context = canvas.getContext('2d');

var canvasSize = 500;
var sectionSize = canvasSize / 3;
canvas.width = canvasSize;
canvas.height = canvasSize;
context.translate(0.5, 0.5);

function getInitialBoard(defaultValue) {
var board = [];

for (var x = 0; x < 3; x++) {
board.push([]);

for (var y = 0; y < 3; y++) {
board[x].push(defaultValue);
}
}

return board;
}

var board = getInitialBoard("");

function addPlayingPiece(mouse) {
var xCordinate;
var yCordinate;

for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
xCordinate = x * sectionSize;
yCordinate = y * sectionSize;

if (
mouse.x >= xCordinate && mouse.x <= xCordinate + sectionSize &&
mouse.y >= yCordinate && mouse.y <= yCordinate + sectionSize
) {

clearPlayingArea(xCordinate, yCordinate);

if (player === 1) {
drawX(xCordinate, yCordinate);
} else {
drawO(xCordinate, yCordinate);
}
}
}
}
}

function clearPlayingArea(xCordinate, yCordinate) {
context.fillStyle = "#191F2B";
context.fillRect(
xCordinate+10,
yCordinate+10,
sectionSize-20,
sectionSize-20
);
}
function drawO(xCordinate, yCordinate) {
var halfSectionSize = (0.5 * sectionSize);
var centerX = xCordinate + halfSectionSize;
var centerY = yCordinate + halfSectionSize;
var radius = (sectionSize - 80) / 2;
var startAngle = 0 * Math.PI;
var endAngle = 2 * Math.PI;

context.lineWidth = 14;
context.strokeStyle = "#7D96BF";
context.beginPath();
context.arc(centerX, centerY, radius, startAngle, endAngle);
context.stroke();
}

function drawX(xCordinate, yCordinate) {
context.strokeStyle = "#7D96BF";

context.beginPath();

var offset = 40;
context.moveTo(xCordinate + offset, yCordinate + offset);
context.lineTo(xCordinate + sectionSize - offset, yCordinate + sectionSize - offset);

context.moveTo(xCordinate + offset, yCordinate + sectionSize - offset);
context.lineTo(xCordinate + sectionSize - offset, yCordinate + offset);
context.lineWidth = 15;


context.stroke();
}

function drawLines(lineWidth, strokeStyle) {
var lineStart = 4;
var lineLenght = canvasSize - 5;
context.lineWidth = lineWidth;
context.lineCap = 'round';
context.strokeStyle = strokeStyle;
context.beginPath();

/*
* Horizontal lines
*/
for (var y = 1; y <= 2; y++) {
context.moveTo(lineStart, y * sectionSize);
context.lineTo(lineLenght, y * sectionSize);
}

/*
* Vertical lines
*/
for (var x = 1; x <= 2; x++) {
context.moveTo(x * sectionSize, lineStart);
context.lineTo(x * sectionSize, lineLenght);
}

context.stroke();
}

drawLines(10, lineColor);

function getCanvasMousePosition(event) {
var rect = canvas.getBoundingClientRect();

return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
}

canvas.addEventListener('mouseup', function (event) {
if (player === 1) {
player = 2;
} else {
player = 1;
}

var canvasMousePosition = getCanvasMousePosition(event);
addPlayingPiece(canvasMousePosition);
drawLines(10, lineColor);
});


var ws = new WebSocket("ws://localhost:8080/ws");

ws.onmessage = function (event) {
var messages = event.data.split('\n');
for (var i = 0; i < messages.length; i++) {
var message = JSON.parse(messages[i]);
onMessage(message);
}
};

function onMessage(message) {
var xCordinate;
var yCordinate;
switch (message.Player) {
case 1:
if (0 <= message.Single && message.Single < 3) {
xCordinate = 0;
yCordinate = message.Single;
}
if (3 <= message.Single && message.Single < 6) {
xCordinate = 1;
yCordinate = message.Single - 3;
}
if (6 <= message.Single && message.Single < 9) {
xCordinate = 2;
yCordinate = message.Single - 6;
}
//clearPlayingArea(xCordinate, yCordinate);
drawX(xCordinate * sectionSize, yCordinate * sectionSize);
break;
case 2:
if (0 <= message.Single && message.Single < 3) {
xCordinate = 0;
yCordinate = message.Single;
}
if (3 <= message.Single && message.Single < 6) {
xCordinate = 1;
yCordinate = message.Single - 3;
}
if (6 <= message.Single && message.Single < 9) {
xCordinate = 2;
yCordinate = message.Single - 6;
}
drawO(xCordinate * sectionSize, yCordinate * sectionSize);
break;
default:
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
clearPlayingArea(x * sectionSize, y * sectionSize);
}
}
break;
}

}
27 changes: 27 additions & 0 deletions example/joshua/htdocs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>

<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" ng-app> <!--<![endif]-->

<head>
<meta charset="utf-8" />

<!-- Set the viewport width to device width for mobile -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Joshua</title>

<!-- Included CSS Files -->
<link rel="stylesheet" href="style.css">

</head>
<body>
<div class="center-wrapper-parent">
<div class="canvas-wrapper">
<canvas id="tic-tac-toe-board" class="center-v"></canvas>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
20 changes: 20 additions & 0 deletions example/joshua/htdocs/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
body{
background-color: #191F2B;
}

#tic-tac-toe-board {
display: block;
margin: 0 auto;
background: #191F2B;
}

.canvas-wrapper {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
}

.canvas-wrapper-parent {
transform-style: preserver-3d;
}
92 changes: 92 additions & 0 deletions example/joshua/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"bufio"
"fmt"
"log"
"os"
"time"

"github.com/gorgonia/agogo"
dual "github.com/gorgonia/agogo/dualnet"
"github.com/gorgonia/agogo/game"
"github.com/gorgonia/agogo/game/mnk"
"github.com/gorgonia/agogo/mcts"

"net/http"
)

func encodeBoard(a game.State) []float32 {
board := agogo.EncodeTwoPlayerBoard(a.Board(), nil)
for i := range board {
if board[i] == 0 {
board[i] = 0.001
}
}
playerLayer := make([]float32, len(a.Board()))
next := a.ToMove()
if next == game.Player(game.Black) {
for i := range playerLayer {
playerLayer[i] = 1
}
} else if next == game.Player(game.White) {
// vecf32.Scale(board, -1)
for i := range playerLayer {
playerLayer[i] = -1
}
}
retVal := append(board, playerLayer...)
return retVal
}

func main() {
conf := agogo.Config{
Name: "Tic Tac Toe",
NNConf: dual.DefaultConf(3, 3, 10),
MCTSConf: mcts.DefaultConfig(3),
UpdateThreshold: 0.52,
}
conf.NNConf.BatchSize = 100
conf.NNConf.Features = 2 // write a better encoding of the board, and increase features (and that allows you to increase K as well)
conf.NNConf.K = 3
conf.NNConf.SharedLayers = 3
conf.MCTSConf = mcts.Config{
PUCT: 1.0,
M: 3,
N: 3,
Timeout: 100 * time.Millisecond,
PassPreference: mcts.DontPreferPass,
Budget: 1000,
DumbPass: true,
RandomCount: 0,
}

outEnc := NewEncoder()
go func(h http.Handler) {
mux := http.NewServeMux()
mux.Handle("/ws", h)
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./htdocs"))))

log.Println("go to http://localhost:8080/static")
http.ListenAndServe(":8080", mux)
}(outEnc)

conf.Encoder = encodeBoard
conf.OutputEncoder = outEnc

g := mnk.TicTacToe()
a := agogo.New(g, conf)
reader := bufio.NewReader(os.Stdin)
fmt.Print("press ented when ready")
reader.ReadString('\n')

//a.Learn(5, 30, 200, 30) // 5 epochs, 50 episode, 100 NN iters, 100 games.
err := a.Learn(5, 50, 100, 100) // 5 epochs, 50 episode, 100 NN iters, 100 games.
if err != nil {
log.Fatal("Fatal: ", err)
}
err = a.Save("example.model")
if err != nil {
log.Fatal("Fatal: ", err)
}
}
Loading