Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tafsiri committed Jul 22, 2020
0 parents commit c07d62a
Show file tree
Hide file tree
Showing 73 changed files with 373,010 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_STORE
tensorboard_logs
dist
.vscode
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# How to Contribute

We are not currently accepting external code contributions to this project. Please feel free to file bugs using GitHub's issue tracker."
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Tic-Tac-Toe the Hard Way

This repository contains code to accompany the podcast ["Tic-Tac-Toe the Hard Way"](https://pair.withgoogle.com/thehardway).

The code provided here is offered as is purely for illustrative purposes for particularly adventurous listeners.
There are two authors for the code in this repository:
- David: A writer
- Yannick: A software engineer

The code is provided with minimal changes or cleanup from how it was originally
written while exploring the topics in the podcast, this is to encourage anyone
just getting started, its okay if your code doesn't look perfect!

This repository acts primarily as accompanying documentation for the podcast and
will remain as is.

> We will not be accepting contributions to this project.
## Requirements & Setup

You will generally need to be comfortable with JavaScript and Node.js to make use
of these repository. If that is not you (and even if it is) we encourage you to
checkout the podcast itself at https://pair.withgoogle.com/thehardway.

There are READMEs the subfolders with further instructions if you are interested in running the code we wrote:
- tic-tac-toe: Contains all the code for the tic-tac-toe experiment
- `tic-tac-toe/training` contains all the training code.
- `tic-tac-toe/viewer` contains all the viewer code.
- tic-tac-__two__: Contains all the code for the tic-tac-two experiment
- `tic-tac-two/training` contains all the training code.
- `tic-tac-two/viewer` contains all the viewer code.

Before running any of the code in those subfolders you should run `npm install` or `yarn` in this folder.

> __This is not an official Google product.__
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "tic-tac-two",
"version": "1.0.0",
"main": "index.js",
"private": true,
"dependencies": {
"clang-format": "^1.4.0",
"typescript": "3.5.3"
}
}
35 changes: 35 additions & 0 deletions tic-tac-toe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Tic-tac-toe experiment

## Training the models

### David's agent

- Install dependencies: run `npm install`
- Generate data and train model: run `npm run generate-and-train`

This will a new folder in the `models/` directory with the saved model. This output can be used in the [viewer](viewer)

### Yannick's agent

- Install dependencies: run `npm install`
- Train model: run `npm run train`

This will a new folder in the `models/` directory with the saved model. This output can be used in the [viewer](viewer)

Training will also write out some metrics to a tensorboard logdir.

Yannick's training code is currently setup to use tfjs-node-gpu. Which requires a
linux machine with CUDA to run. If you want to run this on a machine without CUDA
(e.g. mac or windows), just replace tfjs-node-gpu with tfjs-node wherever you see it.

## Seeing the results

The [viewer](viewer) folder contains a simple web app to see the results of the game. To see it, launch a webserver in this folder. e.g.

```
python -m SimpleHTTPServer 8000
```

Then go to `localhost:8000/viewer` in your browser.

If you want to change the model loaded by each agent in the viewer, edit _david_agent.js_ or _yannick_agent.js_ respectively.
43 changes: 43 additions & 0 deletions tic-tac-toe/shared/agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

export class AbstractAgent {
/**
* Creates an agent that can play
* options is of the form: { symbol: 'x' }
*/
constructor(options) {
this.symbol = options.symbol;
this.name = options.name || this.constructor.name;
}

/**
* An initialization function that can be used to load state that may
* take a while to complete.
*/
async init() {
return;
}

/**
* Takes a boardState and returns a move. A move is an object
* of the form {symbol: 'x'|'o', position: number}
* @param {Object} boardState
* @returns {symbol: 'x'|'o', position: number} move
*/
move(boardState) {}
}
124 changes: 124 additions & 0 deletions tic-tac-toe/shared/game_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

export const BOARD_EMPTY_VAL = -1;

export function checkForWinner(boardState) {
const rows = 3;
const cols = 3;
const stride = 3;

function checkCells(boardState, indices) {
const cell1 = boardState[indices[0]];
const cell2 = boardState[indices[1]];
const cell3 = boardState[indices[2]];

if (cell1 != -1 && cell1 === cell2 && cell2 === cell3) {
return {
hasWinner: true, winningSymbol: cell1, winningCells: indices,
}
} else {
return {
hasWinner: false,
}
}
}

// Check the rows
for (let i = 0; i < (rows * stride); i += stride) {
const indices = [
i,
i + 1,
i + 2,
];
const result = checkCells(boardState, indices);
if (result.hasWinner) {
return result;
}
}

// Check the columns
for (let i = 0; i < cols; i++) {
const indices = [
i,
i + stride,
i + (stride * 2),
];
const result = checkCells(boardState, indices);
if (result.hasWinner) {
return result;
}
}

// Check Diagonal 1, [0,0], [1,1], [2,2];
{
const indices = [
0,
1 + (1 * stride),
2 + (2 * stride),
];
const result = checkCells(boardState, indices);
if (result.hasWinner) {
return result;
}
}

// Check Diagonal 2, [0,2], [1,1], [2,0],
{
const indices = [
stride - 1,
1 + (1 * stride),
0 + (2 * stride),
];

const result = checkCells(boardState, indices);
if (result.hasWinner) {
return result;
}
}

return {
hasWinner: false,
};
}

export function checkForIllegalMove(boardState, move) {
if (boardState[move.position] !== BOARD_EMPTY_VAL || move.position < 0 ||
move.position > 8) {
return {
hasError: true,
errorCell: move.position,
};
}

return {
hasError: false,
};
}

export function checkDone(boardState) {
const isDone = boardState.every(symbol => symbol != -1);
return isDone;
}

export async function wait(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
}
44 changes: 44 additions & 0 deletions tic-tac-toe/shared/random_agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import {AbstractAgent} from './agent.js';

export class RandomAgent extends AbstractAgent {
constructor(options) {
super(options);
}

move(boardState) {
const freeIndices = boardState.reduce((memo, val, idx) => {
if (val === -1) {
memo.push(idx);
}
return memo;
}, []);

if (freeIndices.length === 0) {
throw new Error('No legal moves');
}

// Select an index to play.
const nextIndex = Math.floor(Math.random() * freeIndices.length);
return {
symbol: this.symbol,
position: freeIndices[nextIndex],
};
}
}
Loading

0 comments on commit c07d62a

Please sign in to comment.