master
Ethan Grantz 4 years ago
commit 7110d73ea6

@ -0,0 +1,20 @@
import http.server
import socketserver
import webbrowser
class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
extensions_map={
".js": "application/x-javascript",
".html": "text/html",
".css": "text/css",
".json": "application/json",
"": "application/octet-stream",
}
PORT = 8000
httpd = socketserver.TCPServer(("", PORT), HTTPRequestHandler)
webbrowser.register('firefox',None,webbrowser.BackgroundBrowser("C:\\Program Files\\Mozilla Firefox\\firefox.exe"))
#input("zonks:")
webbrowser.get('firefox').open_new_tab("http://localhost:8000")
httpd.serve_forever()

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>Snake</title>
<link rel="stylesheet" href="./src/style/css.css" />
</head>
<body>
<div id="vuewrapper">
<top-comp></top-comp>
</div>
<script src='./lib/vue.js'></script>
<script src='./lib/vuetranslit.js' type='module'></script>
<script src='./src/main.js' type='module'></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,72 @@
export default class Transliterator {
constructor(components) {
this.components = components;
this.execute();
}
execute() {
for (const Comp of this.components) {
const method_list = Object.getOwnPropertyNames(Comp.prototype);
const watch = {}
const methods = {}
const computed = {}
for (let x of method_list) {
if (x.startsWith("watch_")) {
let name = x.substr(6);
const handler = function(...args) {
return Comp.prototype[x].bind(this)(...args);
}
watch[name] = handler;
} else if (x.startsWith("compute_")) {
let name = x.substr(8);
const handler = function(...args) {
return Comp.prototype[x].bind(this)(...args);
}
computed[name] = handler;
} else {
const handler = function(...args) {
return Comp.prototype[x].bind(this)(...args);
}
methods[x] = handler;
}
}
Vue.component(Comp.tag,{
props: Comp.props,
template: Comp.template,
data: () => {
return new Comp();
},
watch,
computed,
methods,
created: function() {
if (typeof this.on_create === 'function') this.on_create();
},
updated: function() {
if (typeof this.on_update === 'function') this.on_update();
},
mounted: function() {
if (typeof this.on_mount === 'function') this.on_mount();
},
destroyed: function() {
if (typeof this.on_destroy === 'function') this.on_destroy();
},
});
}
}
}
export class Component {
static get tag() { return ""; }
static get props() { return {} }
static get template() { return ""; }
on_create() {}
on_update() {}
on_mount() {}
on_destroy() {}
}

@ -0,0 +1,28 @@
import {GameState,clone} from './util/util.js'
class GameApplication {
rowCount = 0;
colCount = 0;
gameWin = 'undefined';
current_state = GameState.SizeSelect;
hasAI = false;
endFunction = null
ready = false;
passFunction = null;
get_current_state() {
return clone(this.current_state);
}
initEnd(victory) {
this.endFunction(this.current_state, victory);
}
tester(hand) {
this.endFunction = hand;
}
}
const app = new GameApplication();
export default app;

@ -0,0 +1,13 @@
//list of imported component names
import CellComponent from './components/CellComponent.js'
import BoardComponent from './components/BoardComponent.js'
import TopComponent from './components/TopComponent.js'
import AIComponent from './components/AIComponent.js'
//put classnames in here
export default [
CellComponent,
BoardComponent,
TopComponent,
AIComponent,
]

@ -0,0 +1,251 @@
import {Component} from '../../lib/vuetranslit.js'
import app from '../application.js'
import {GameState, snakeDirection} from '../util/util.js'
const template = `<div></div>`
class AIComponent extends Component {
static get tag() { return 'ai'; }
static get template() { return template; }
static get props() { return {}; }
ready = app;
hamiltonCircuit = [];
move_snake = null;
set_sD = null;
async play() {
const start = parseInt(app.rowCount / 2);
this.createHamiltonCircuit(app.rowCount, app.colCount, start + ',2');
let currInstruction = this.hamiltonCircuit.length - 1;
while (app.get_current_state() === GameState.Gameplay) {
let nextLoc;
if (currInstruction === this.hamiltonCircuit.length - 1) nextLoc = 0;
else nextLoc = currInstruction + 1;
this.travel(currInstruction,nextLoc);
currInstruction = nextLoc;
await this.sleep(Math.ceil(5000/(app.rowCount * app.colCount)));
}
}
travel(point1,point2) {
const p1 = this.hamiltonCircuit[point1].split(',');
const p2 = this.hamiltonCircuit[point2].split(',');
if (parseInt(p1[0]) < parseInt(p2[0])) {
this.set_sD(snakeDirection.Down);
} else if (parseInt(p1[0]) > parseInt(p2[0])) {
this.set_sD(snakeDirection.Up);
} else if (parseInt(p1[1]) < parseInt(p2[1])) {
this.set_sD(snakeDirection.Right);
} else if (parseInt(p1[1]) > parseInt(p2[1])) {
this.set_sD(snakeDirection.Left);
}
this.move_snake();
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve,ms));
}
createHamiltonCircuit(rows,cols,start) {
const getLast = () => { return this.hamiltonCircuit[this.hamiltonCircuit.length - 1]; }
this.move_hor(1,start);
if (cols % 2) {
/*
10x7: top zigzag, vertical zigzag, full bottom zigzag
10x9: top zigzag, vertical zigzag, full bottom zigzag
*/
if (rows % 4) {
while (true) {
if (parseInt(getLast().split(',')[0]) === 1) break;
this.move_hor_until(1,cols - 3,getLast());
this.move_vert(-1,getLast());
if (parseInt(getLast().split(',')[0]) === 1) break;
this.move_hor_until(-1,0,getLast());
this.move_vert(-1,getLast());
}
while (true) {
if (parseInt(getLast().split(',')[1]) === cols - 2) break;
this.move_vert(-1,getLast());
this.move_hor(1,getLast());
if (parseInt(getLast().split(',')[1]) === cols - 2) break;
this.move_vert(1,getLast());
this.move_hor(1,getLast());
}
while (true) {
if (!this.move_hor(1,getLast())) break;
if (!this.move_vert(1,getLast())) break;
if (!this.move_hor(-1,getLast())) break;
if (!this.move_vert(1,getLast())) break;
}
while (true) {
this.move_hor(-1,getLast());
if (parseInt(getLast().split(',')[1]) === 0) break;
this.move_vert_until(-1,parseInt(start.split(',')[0]) + 1, getLast());
this.move_hor(-1,getLast());
if (parseInt(getLast().split(',')[1]) === 0) break;
this.move_vert_until(1,rows - 1, getLast());
}
}
else {
if ((cols - 1) % 4) {
while (true) {
if (parseInt(getLast().split(',')[0]) === 0) break;
this.move_hor_until(1,cols - 3, getLast());
this.move_vert(-1,getLast());
if (parseInt(getLast().split(',')[0]) === 0) break;
this.move_hor_until(-1,0, getLast());
this.move_vert(-1,getLast());
}
this.move_hor_until(1,cols - 1,getLast());
while (true) {
this.move_vert(1,getLast());
if (parseInt(getLast().split(',')[0]) === parseInt(start.split(',')[0]) + 1) break;
this.move_hor(-1,getLast());
this.move_vert(1,getLast());
if (parseInt(getLast().split(',')[0]) === parseInt(start.split(',')[0]) + 1) break;
this.move_hor(1,getLast());
}
}
else {
while (true) {
if (parseInt(getLast().split(',')[0]) === 0) break;
this.move_hor_until(1,cols - 2, getLast());
this.move_vert(-1,getLast());
if (parseInt(getLast().split(',')[0]) === 0) break;
this.move_hor_until(-1,0, getLast());
this.move_vert(-1,getLast());
}
this.move_hor_until(1,cols-1,getLast());
this.move_vert_until(1,parseInt(start.split(',')[0]) + 1, getLast());
}
while (true) {
if (parseInt(getLast().split(',')[0]) === rows - 1) break;
this.move_hor_until(-1,1,getLast());
this.move_vert(1,getLast());
if (parseInt(getLast().split(',')[0]) === rows - 1) break;
this.move_hor_until(1,cols-1,getLast());
this.move_vert(1,getLast());
}
this.move_hor_until(-1,0,getLast());
}
this.move_vert_until(-1,parseInt(start.split(',')[0]),getLast());
this.move_hor_until(1,parseInt(start.split(',')[1]),getLast());
}
else {
if ((rows - parseInt(start.split(',')[0]) - (rows % 2)) % 2) {
while (true) {
if (parseInt(getLast().split(',')[0]) === 1) break;
this.move_hor_until(1,cols - 2,getLast());
this.move_vert(-1,getLast());
if (parseInt(getLast().split(',')[0]) === 1) break;
this.move_hor_until(-1,0, getLast());
this.move_vert(-1,getLast());
}
while (true) {
if (!this.move_vert(-1,getLast())) break;
if (!this.move_hor(1,getLast())) break;
if (!this.move_vert(1,getLast())) break;
if (!this.move_hor(1,getLast())) break;
}
}
else {
while (true) {
if (parseInt(getLast().split(',')[0]) === 0) break;
this.move_hor_until(1,cols - 2,getLast());
this.move_vert(-1,getLast());
if (parseInt(getLast().split(',')[0]) === 0) break;
this.move_hor_until(-1,0, getLast());
this.move_vert(-1,getLast());
}
this.move_hor_until(1,cols - 1, getLast());
}
this.move_vert_until(1,parseInt(start.split(',')[0]) + 1,getLast());
if ((rows - parseInt(start.split(',')[0]) - 1) % 2) {
while (true) {
if (parseInt(getLast().split(',')[0]) === rows - 1) break;
this.move_hor_until(-1,1,getLast());
this.move_vert(1,getLast());
if (parseInt(getLast().split(',')[0]) === rows - 1) break;
this.move_hor_until(1,cols - 1,getLast());
this.move_vert(1,getLast());
}
this.move_hor_until(-1,0,getLast());
}
else {
while (true) {
if (parseInt(getLast().split(',')[0]) === rows - 2) break;
this.move_hor_until(-1,1,getLast());
this.move_vert(1,getLast());
if (parseInt(getLast().split(',')[0]) === rows - 2) break;
this.move_hor_until(1,cols - 1,getLast());
this.move_vert(1,getLast());
}
while (true) {
if (!this.move_vert(1,getLast())) break;
if (!this.move_hor(-1,getLast())) break;
if (!this.move_vert(-1,getLast())) break;
if (!this.move_hor(-1,getLast())) break;
}
}
this.move_vert_until(-1,parseInt(start.split(',')[0]),getLast());
this.move_hor_until(1,parseInt(start.split(',')[1]),getLast());
}
}
move_vert_until(dir,row,start) {
let lastLoc = start.split(',');
while (parseInt(lastLoc[0]) != row) {
let nextLoc = (parseInt(lastLoc[0]) + dir) + ',' + lastLoc[1];
this.hamiltonCircuit.push(nextLoc);
lastLoc = nextLoc.split(',');
}
}
move_hor_until(dir, col, start) {
let lastLoc = start.split(',');
while (parseInt(lastLoc[1]) != col) {
let nextLoc = lastLoc[0] + ',' + (parseInt(lastLoc[1]) + dir);
this.hamiltonCircuit.push(nextLoc);
lastLoc = nextLoc.split(',');
}
}
move_vert(dir,start) {
const currLoc = start.split(',');
if (parseInt(currLoc[0]) + dir > -1 && parseInt(currLoc[0]) + dir < app.rowCount) {
const newLoc = (parseInt(currLoc[0]) + dir) + ',' + currLoc[1];
if (this.hamiltonCircuit.filter(x => x === newLoc).length === 0) {
this.hamiltonCircuit.push(newLoc);
return true;
}
}
return false;
}
move_hor(dir,start) {
const currLoc = start.split(',');
if (parseInt(currLoc[1]) + dir > -1 && parseInt(currLoc[1]) + dir < app.colCount) {
const newLoc = currLoc[0] + ',' + (parseInt(currLoc[1]) + dir);
if (this.hamiltonCircuit.filter(x => x === newLoc).length === 0) {
this.hamiltonCircuit.push(newLoc);
return true;
}
}
return false;
}
on_create() {
this.set_sD = app.passFunction[1];
this.move_snake = app.passFunction[0];
this.play();
}
}
export default AIComponent;

@ -0,0 +1,126 @@
import {Component} from '../../lib/vuetranslit.js'
import {snakeDirection as sD, CellState, GameState} from '../util/util.js'
import app from '../application.js'
const template = `
<div class="grid-container">
<div class='gridrow' v-for="i in rows">
<cell-comp v-for="j in cols" :key="(i-1) + ',' + (j-1)" :ref="(i-1) + ',' + (j-1)" :size='cellsize'></cell-comp>
</div>
</div>`
class BoardComponent extends Component {
static get tag() { return 'board-comp'; }
static get template() { return template; }
static get props() {
return {
rows: Number,
cols: Number,
cellsize: Number,
};
}
bound_listener = null;
snake = [];
snakeDirection = sD.Right;
ateFood = false;
async on_create() {
const start = parseInt(app.rowCount / 2);
this.snake = [start + ",2", start + ",1", start + ",0"];
const keydown_fn = this.on_keydown.bind(this);
this.bound_listener = keydown_fn;
window.addEventListener('keydown', keydown_fn);
app.passFunction = [this.move_snake,this.set_snake_direction];
app.ready = true;
}
async on_destroy() {
const keydown_fn = this.bound_listener;
window.removeEventListener('keydown', keydown_fn);
}
async on_mount() {
for (let i of this.snake) {
this.$refs[i][0].set_current_state(CellState.Snake);
}
this.place_food();
}
move_snake() {
if ((this.snakeDirection === sD.Up && this.snake[0].split(',')[0] === '0') ||
(this.snakeDirection === sD.Down && this.snake[0].split(',')[0] == app.rowCount - 1) ||
(this.snakeDirection === sD.Left && this.snake[0].split(',')[1] === '0') ||
(this.snakeDirection === sD.Right && this.snake[0].split(',')[1] == app.colCount - 1)) {
app.current_state = GameState.Fin;
console.log("wall")
app.initEnd(this.test_for_win());
return;
}
let nextSpace = this.snake[0].split(',');
if (this.snakeDirection === sD.Up) {
nextSpace[0] = parseInt(nextSpace[0]) - 1;
} else if (this.snakeDirection === sD.Down) {
nextSpace[0] = parseInt(nextSpace[0]) + 1;
} else if (this.snakeDirection === sD.Left) {
nextSpace[1] = parseInt(nextSpace[1]) - 1;;
} else if (this.snakeDirection === sD.Right) {
nextSpace[1] = parseInt(nextSpace[1]) + 1;;
}
nextSpace = nextSpace[0] + ',' + nextSpace[1];
if (this.snake.some(x => x === nextSpace)) {
app.current_state = GameState.Fin;
app.initEnd(this.test_for_win());
return;
}
if (this.$refs[nextSpace][0].get_current_state() === "food") {
this.ateFood = true;
}
this.snake.unshift(nextSpace);
this.$refs[nextSpace][0].set_current_state(CellState.Snake);
if (!this.ateFood) {
this.$refs[this.snake[this.snake.length - 1]][0].set_current_state(CellState.Empty);
this.snake.pop();
} else {
this.place_food();
this.ateFood = false;
}
}
place_food() {
if (this.test_for_win()) return;
let loc = Math.floor(Math.random() * app.rowCount) + ',' + Math.floor(Math.random() * app.colCount);
while (this.$refs[loc][0].get_current_state() !== CellState.Empty) {
loc = Math.floor(Math.random() * app.rowCount) + ',' + Math.floor(Math.random() * app.colCount);
}
this.$refs[loc][0].set_current_state(CellState.Food);
}
set_snake_direction(dir) {
this.snakeDirection = dir;
}
test_for_win() {
return this.snake.length == app.rowCount * app.colCount;
}
on_keydown(e) {
if (!app.hasAI) {
if (e.key === 'ArrowUp' && this.snakeDirection !== sD.Down) {
this.snakeDirection = sD.Up;
} else if (e.key === 'ArrowDown' && this.snakeDirection !== sD.Up) {
this.snakeDirection = sD.Down;
} else if (e.key === 'ArrowLeft' && this.snakeDirection !== sD.Right) {
this.snakeDirection = sD.Left;
} else if (e.key === 'ArrowRight' && this.snakeDirection !== sD.Left) {
this.snakeDirection = sD.Right;
} else return;
this.move_snake();
}
}
}
export default BoardComponent;

@ -0,0 +1,38 @@
import {Component} from '../../lib/vuetranslit.js'
import {CellState, clone} from '../util/util.js'
const template = `
<div
class='cell-component'
v-bind:class='{ emptycell: state === CellState.Empty, snakecell: state === CellState.Snake, foodcell: state === CellState.Food }'
:style="{width: size + 'px', height: size + 'px'}">
</div>
`
class CellComponent extends Component {
CellState = CellState;
static get tag() { return 'cell-comp'; }
static get template() { return template; }
static get props() {
return {
size: {
type: Number,
default: 30
}
}
}
state = CellState.Empty;
set_current_state(stateVar) {
this.state = stateVar;
}
get_current_state() {
return clone(this.state);
}
}
export default CellComponent;

@ -0,0 +1,92 @@
import {Component} from '../../lib/vuetranslit.js'
import {GameState, instructions} from '../util/util.js'
import app from '../application.js'
const template = `
<div class='top-level-container'>
<div v-if="current_state === GameState.SizeSelect">
<div>
<hr class='separator--line'/>
<h1 class='header_title' id="test">Snake</h1>
<hr class='separator--dot' />
</div>
<div id="diminput">
<span class="instructions" v-if='instructions'>{{instructions}}</span><br />
<input type="number" v-model="rowinput"/>
<input type="number" v-model="colinput"/>
</div>
<button id='ai-btn' v-bind:class="{ hasai: has_ai, noai: !has_ai }" @click='set_has_ai()'>AI?</button>
<button id='start-btn' v-if="accept_bounds" @click='set_game_state(GameState.Gameplay)'>Start</button>
</div>
<div v-if="current_state === GameState.Gameplay">
<board-comp ref="board" v-bind:rows="rowinput" v-bind:cols="colinput" v-bind:cellsize="celldim"></board-comp>
<ai v-if="has_ai"></ai>
</div>
<div v-if="current_state === GameState.Fin">
<span class="instructions" id="endcredits" v-if='instructions'>{{instructions.replace('{end}', victory)}}</span>
</div>
</div>`
class TopComponent extends Component {
GameState = GameState
static get tag() { return 'top-comp'; }
static get template() { return template; }
static get props() { return {}; }
current_state = GameState.SizeSelect;
rowinput = 6;
colinput = 6;
has_ai = app.hasAI;
instructions = instructions[this.current_state];
victory = "";
watch_rowinput() {
this.rowinput = parseInt(this.rowinput);
app.rowCount = this.rowinput;
}
watch_colinput() {
this.colinput = parseInt(this.colinput);
app.colCount = this.colinput;
}
watch_current_state() {
this.instructions = instructions[this.current_state];
}
compute_celldim() {
let height = parseInt((window.screen.height - 150) / this.rowinput);
let width = parseInt(window.screen.width / this.colinput);
return Math.min(width, height);
}
compute_accept_bounds() {
return this.rowinput > 5 && this.colinput > 5 && (!(this.rowinput % 2) || !(this.colinput % 2));
}
set_game_state(state) {
this.current_state = state;
app.current_state = state;
}
set_has_ai() {
app.hasAI = !app.hasAI;
this.has_ai = app.hasAI;
}
on_create() {
app.rowCount = this.rowinput;
app.colCount = this.colinput;
this.current_state = app.get_current_state();
app.tester(async (state,victory) => {
this.current_state = state;
this.instructions = instructions[this.current_state];
if (victory) this.victory = 'win';
else this.victory = 'lose';
})
}
}
export default TopComponent;

@ -0,0 +1,11 @@
import componentlist from './componentlist.js'
import Transliterator from '../lib/vuetranslit.js'
const translit = new Transliterator(componentlist);
const vueapp = new Vue({
el: '#vuewrapper',
data: {},
});
export {vueapp, translit};

@ -0,0 +1,191 @@
#vuewrapper {
height: 100%;
width: 100%;
position: fixed;
top: 0;
left: 0;
overflow: hidden;
background-color: #182025;
}
.header_title {
text-align: center;
letter-spacing: .1em;
color: #ec8d2e;
}
.top-level-container {
text-align: center;
}
.gridrow {
display: flex;
justify-content: center;
overflow: hidden;
}
.grid-container {
margin: 10px;
justify-content: center;
}
#diminput {
color: #fff;
font-size: 20px;
}
#diminput input {
border: 1px solid #204a61;
background: none;
color: #fff;
font-size: 20px;
text-align:center;
padding: 10px 0px;
width: 5%;
margin: 10px;
position: relative;
overflow: hidden;
}
#start-btn {
/*width: 70px;
height: 20px;
margin: 10px;*/
border: none;
}
#ai-btn {
/*width: 70px;
height: 20px;
margin: 10px;*/
border: none;
}
.hasai {
background-color: green;
}
.noai {
background-color: red;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type=number] {
-moz-appearance: textfield;
}
.instructions {
white-space: pre;
}
#endcredits {
color: white;
font-size: 3em;
display: flex;
justify-content: center;
align-content: center;
margin: 100px;
padding: 100px;
}
/*********CELLS**********
*************************/
.cell-component {
}
.cell-component.emptycell {
background-color: #182025;
border: 1px solid #1d272e;
}
.cell-component.snakecell {
background-color: #05ff3f;
border: 1px solid #18b53c;
}
.cell-component.foodcell {
background-color: red;
border: 1px solid darkred;
}
/*******END CELLS********
*************************/
/********HR LINES********
*************************/
.separator--line {
border: 0;
border-bottom: 5px solid #ec8d2e;
width: 0;
-webkit-animation: separator-width 1s ease-out forwards;
animation: separator-width 1s ease-out forwards;
}
.separator--dot {
border: 0;
border-bottom: 5px dotted #ec8d2e;
width: 0;
-webkit-animation: separator-width 1.5s ease-out forwards;
animation: separator-width 1.5s ease-out forwards;
}
@-webkit-frames separator-width {
0% {
width: 0;
}
100% {
width: 100%;
}
}
@keyframes separator-width {
0% {
width: 0;
}
100% {
width: 100%;
}
}
@-webkit-keyframes dot-move-right {
0% {
left: 0;
}
100% {
left: 32px;
}
}
@keyframes dot-move-right {
0% {
left: 0;
}
100% {
left: 32px;
}
}
@-webkit-keyframes dot-move-left {
0% {
left: 0;
}
100% {
left: -32px;
}
}
@keyframes dot-move-left {
0% {
left: 0;
}
100% {
left: -32px;
}
}
/****END OF HR LINES*****
*************************/

@ -0,0 +1,57 @@
/**@module util*/
/**
* Enum, all cell states
*/
export const CellState = {
//No snake, no food
Empty: 'empty',
//Snake
Snake: 'snake',
//Food
Food: 'food',
}
/**
* Enum, Game states
*/
export const GameState = {
//Choose grid size
SizeSelect: 'sizeselect',
//actual game
Gameplay: 'gameplay',
//end game
Fin: 'fin',
}
export const instructions = {
[GameState.SizeSelect]: "Decide on the size of the Board\nRows -- Cols",
[GameState.Gameplay]: "",
[GameState.Fin]: "You {end}!",
}
export const snakeDirection = {
Up: 'up',
Down: 'down',
Left: 'left',
Right: 'right',
}
export function clone(obj) {
//not an object or array
if (obj === null || typeof obj !== 'object') return obj;
//is an array
if (Array.isArray(obj)) return obj.map(x => clone(x));
//is an object
const copy = {};
for (const prop in obj) {
copy[prop] = clone(obj[prop]);
}
return copy;
}
Loading…
Cancel
Save