Skip to content

Instantly share code, notes, and snippets.

@spencer-p
Created June 5, 2017 05:37
Show Gist options
  • Save spencer-p/828b9889efd147b2488c6528e5e027c1 to your computer and use it in GitHub Desktop.
Save spencer-p/828b9889efd147b2488c6528e5e027c1 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<html>
<head>
<!--
Customize this policy to fit your own app's needs. For more guidance, see:
https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
Some notes:
* gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
* https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
* Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
* Enable inline JS: add 'unsafe-inline' to default-src
-->
<meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' https://luca-ucsc-teaching-backend.appspot.com 'unsafe-inline'; script-src 'self' https://luca-ucsc-teaching-backend.appspot.com 'unsafe-inline' 'unsafe-eval';">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<link href="css/myapp.css" rel="stylesheet" type="text/css" />
<link href="css/stupid.css" rel="stylesheet" type="text/css"/>
<link href="font-awesome-4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<title>Cordova Starter App, CMPS 121</title>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript" src="js/randomString.js"></script>
<script type="text/javascript" src="js/generate_board.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</head>
<body>
<div id="vue-div" class="app" style="display:none">
<div class="top_input centered padded">
<input v-model="magic_word" placeholder="shared magic word" />
<button v-on:click="set_magic_word">Play</button>
<button v-on:click="new_game">New Game</button>
<i v-if="need_new_magic_word" class="fa fa-warning"></i>
</div>
<div class="padded centered">
<div class="status_line">
<b>${win_line}</b> ${status_line}
</div>
<div class="turn">
It is <b v-if="!is_my_turn">not </b>your turn.
</div>
</div>
<div class="board container padded centered">
<table>
<tr v-for="i in [0, 1, 2, 3, 4, 5, 6, 7]">
<td v-on:click="play(i,j)" v-for="j in [0, 1, 2, 3, 4, 5, 6, 7]"
v-bind:class="{ red: opponent_board()[i*8+j] === 'h' }">
<template v-if="opponent_board()[i*8+j] === 'w'">
<i class="fa fa-circle" style="color:blue"></i>
</template>
</td>
</tr>
</table>
</div>
<div class="board container padded centered">
<table>
<tr v-for="i in [0, 1, 2, 3, 4, 5, 6, 7]">
<td v-for="j in [0, 1, 2, 3, 4, 5, 6, 7]"
v-bind:class="{ red: own_board()[8*i+j] === 'h',
green: typeof(own_board()[8*i+j]) === 'number',
blue: own_board()[8*i+j] === 'w'}">
</td>
</tr>
</table>
</div>
</div>
<script type="text/javascript" src="cordova.js"></script>
</body>
</html>
var app = function() {
var self = {};
self.is_configured = false;
var server_url = "https://luca-ucsc-teaching-backend.appspot.com/keystore/";
var call_interval = 2000;
Vue.config.silent = false; // show all warnings
// Extends an array
self.extend = function(a, b) {
for (var i = 0; i < b.length; i++) {
a.push(b[i]);
}
};
self.my_identity = randomString(20);
// Function creates null board of length or 8x8
self.null_board = function(length) {
if (length === undefined) {
length = 8*8;
}
var array = Array(length);
for (var i = 0; i < length; i++) {
array[i] = "";
}
return array;
};
// Enumerates an array.
var enumerate = function(v) {
var k=0;
v.map(function(e) {e._idx = k++;});
};
// Initializes an attribute of an array of objects.
var set_array_attribute = function (v, attr, x) {
v.map(function (e) {e[attr] = x;});
};
self.initialize = function () {
document.addEventListener('deviceready', self.ondeviceready, false);
};
self.ondeviceready = function () {
// This callback is called once Cordova has finished its own initialization.
console.log("The device is ready");
$("#vue-div").show();
self.is_configured = true;
};
// This is the object that contains the information coming from the server.
self.player_1 = null;
self.player_2 = null;
// This is the main control loop.
function call_server() {
if (self.vue.chosen_magic_word === null) {
console.log("No magic word.");
setTimeout(call_server, call_interval);
} else {
// We can do a server call.
console.log("Calling the server");
$.ajax({
dataType: 'json',
url: server_url +'read',
data: {key: "SPJPETER"+self.vue.chosen_magic_word},
success: self.process_server_data,
complete: setTimeout(call_server, call_interval) // Here we go again.
});
}
}
// Main function for sending the state.
self.send_state = function () {
$.post(server_url + 'store',
{
key: "SPJPETER"+self.vue.chosen_magic_word,
val: JSON.stringify(
{
'player_1': self.player_1,
'player_2': self.player_2,
'board_1': self.vue.board_1,
'board_2': self.vue.board_2,
'turn_count': self.vue.turn_count,
'game_count': self.vue.game_count
}
)
}
);
};
// Main place where we receive data and act on it.
self.process_server_data = function (data) {
// Check for no data
if (!data.result) {
self.player_1 = self.my_identity;
self.player_2 = null;
self.vue.board_1 = getBoard();
self.vue.board_2 = self.null_board();
self.vue.turn_count = 0;
self.vue.game_count = 0;
self.send_state();
}
else { // Do have data
var answer = JSON.parse(data.result);
if (answer.player_2 === null) {
if (answer.player_1 === self.my_identity) {
self.vue.status_line = "Wating for second player.";
return;
}
else if (answer.player_1 === null) {
self.vue.status_line = "Other player left. Wating for second player.";
self.player_1 = self.my_identity;
self.player_2 = null;
self.vue.board_1 = getBoard();
self.vue.board_2 = self.null_board();
self.vue.turn_count = 0;
self.vue.game_count = 0;
self.vue.is_my_turn = false;
self.send_state();
}
else {
self.vue.status_line = "Joining the game.";
// Set data and send state
self.player_1 = answer.player_1;
self.player_2 = self.my_identity;
self.vue.board_1 = answer.board_1;
self.vue.board_2 = getBoard();
self.vue.turn_count = answer.turn_count;
self.vue.game_count = answer.game_count;
self.send_state();
// Make it our turn
self.vue.is_my_turn = true;
}
}
else { // both not null
// check if both players are already in
if (self.player_1 !== self.my_identity && self.player_2 !== self.my_identity) {
self.vue.status_line = "Cannot join this game, two players already present.";
self.vue.need_new_magic_word = true;
}
else { // Potentially new turn data
self.vue.status_line = "Both players present and transferring data.";
if (answer.turn_count >= self.vue.turn_count && answer.game_count === self.vue.game_count) {
// Swap turns
if (answer.turn_count > self.vue.turn_count) {
self.vue.is_my_turn = !self.vue.is_my_turn;
}
self.vue.turn_count = answer.turn_count;
self.vue.board_1 = answer.board_1;
self.vue.board_2 = answer.board_2;
self.player_1 = answer.player_1;
self.player_2 = answer.player_2;
if (self.board_is_won(self.own_board())) {
self.vue.win_line = "Sorry, you lost.";
}
}
else if (answer.game_count > self.vue.game_count) {
self.vue.status_line = "Starting a new game";
self.vue.win_line = "";
if (self.player_1 === self.my_identity) {
self.vue.board_2 = answer.board_2;
self.vue.board_1 = getBoard();
}
else {
self.vue.board_1 = answer.board_1;
self.vue.board_2 = getBoard();
}
self.vue.turn_count = answer.turn_count;
self.vue.game_count = answer.game_count;
self.send_state();
// Make it our turn
self.vue.is_my_turn = true;
}
}
}
}
/* dont do this
return;
// If data is null, we send our data.
if (!data.result) {
self.player_x = self.my_identity;
self.player_o = null;
self.vue.board_1 = self.null_board();
self.vue.board_2 = self.null_board();
self.vue.is_my_turn = false;
self.send_state();
} else {
// I technically don't need to assign this to self, but it helps debug the code.
self.server_answer = JSON.parse(data.result);
self.player_x = self.server_answer.player_x;
self.player_o = self.server_answer.player_o;
if (self.player_x === null || self.player_o === null) {
// Some player is missing. We cannot play yet.
self.vue.is_my_turn = false;
console.log("Not all players present.");
if (self.player_o === self.my_identity || self.player_x === self.my_identity) {
// We are already present, nothing to do.
console.log("Waiting for other player to join");
} else {
console.log("Signing up now.");
// We are not present. Let's join if we can.
if (self.player_x === null) {
// Preferentially we play as x.
self.player_x = self.my_identity;
self.send_state();
} else if (self.player_o === null) {
self.player_o = self.my_identity;
self.send_state();
} else {
// The magic word is already taken.
self.vue.need_new_magic_word = true;
}
}
} else {
console.log("Both players are present");
// Both players are present.
// Let us determine our role if any.
if (self.player_o !== self.my_identity && self.player_x !== self.my_identity) {
// Again, we are intruding in a game.
self.vue.need_new_magic_word = true;
} else {
// Here is the interesting code: we are playing, and the opponent is there.
// Reconciles the state.
self.update_local_vars(self.server_answer);
}
}
}*/
};
self.update_local_vars = function (server_answer) {
// First, figures out our role.
if (server_answer.player_o === self.my_identity) {
self.vue.my_role = 'o';
} else if (server_answer.player_x === self.my_identity) {
self.vue.my_role = 'x';
} else {
self.vue.my_role = ' ';
}
// Reconciles the board, and computes whose turn it is.
for (var i = 0; i < 9; i++) {
if (self.vue.board[i] === ' ' || server_answer.board[i] !== ' ') {
// The server has new information for this board.
Vue.set(self.vue.board, i, server_answer.board[i]);
} else if (self.vue.board[i] !== server_answer.board[i]
&& self.vue.board[i] !== ' ' && server_answer.board[i] !== ' ') {
console.log("Board inconsistency at: " + i);
console.log("Local:" + self.vue.board[i]);
console.log("Server:" + server_answer.board[i]);
}
}
// Compute whether it's my turn on the basis of the now reconciled board.
self.vue.is_my_turn = (self.vue.board !== null) &&
(self.vue.my_role === whose_turn(self.vue.board));
};
function whose_turn(board) {
num_x = 0;
num_o = 0;
for (var i = 0; i < 9; i++) {
if (board[i] === 'x') num_x += 1;
if (board[i] === 'o') num_o += 1;
}
if (num_o >= num_x) {
return 'x';
} else {
return 'o';
}
}
self.set_magic_word = function () {
if (self.vue.chosen_magic_word === self.vue.magic_word) { return; }
// reset board if active
if (self.vue.chosen_magic_word !== null && self.player_1 !== self.my_identity && self.player_2 !== self.my_identity) {
self.player_1 = null;
self.player_2 = null;
self.vue.board_1 = null;
self.vue.board_2 = null;
self.vue.turn_count = null;
self.vue.game_count = null;
self.vue.need_new_magic_word = false;
self.send_state();
}
self.vue.chosen_magic_word = self.vue.magic_word;
self.vue.need_new_magic_word = false;
// Resets board and turn.
self.vue.board_1 = self.null_board();
self.vue.board_2 = self.null_board();
self.vue.is_my_turn = false;
self.vue.my_role = "";
};
self.play = function (i, j) {
var opponent_board = self.opponent_board();
if (self.vue.is_my_turn && opponent_board[8*i+j] !== 'h' && opponent_board[8*i+j] !== 'w') {
if (typeof(opponent_board[8*i+j]) === "number") {
// This is a hit
// Check if the ship is sunk
var shipid = opponent_board[8*i+j];
var shipcount = 0;
for (var index = 0; index < opponent_board.length; index++) {
if (opponent_board[index] === shipid) {
shipcount++;
}
}
//console.log("set h");
//opponent_board.splice(8*i+j, 1, 'h');
opponent_board[8*i+j] = 'h';
if (shipcount <= 1) { // Hit last of the ship
self.reveal_water(opponent_board, i, j);
}
}
else if (opponent_board[8*i+j] === '') {
// Miss (water)
//opponent_board.splice(8*i+j, 1, 'w');
opponent_board[8*i+j] = 'w';
}
self.vue.is_my_turn = false;
self.vue.turn_count++;
// Check for win
if (self.board_is_won(opponent_board)) {
self.vue.win_line = "You won!";
}
self.send_state();
}
};
self.reveal_water = function(board, x, y) {
var dirs = [{x: 1, y: 0}, {x: -1, y: 0}, {x: 0, y: 1}, {x: 0, y: -1}];
var dir;
for (var i = 0; i < dirs.length; i++) {
if (board[8*(x+dirs[i].x)+(y+dirs[i].y)] === 'h') {
dir = dirs[i];
break;
}
}
if (dir === undefined) { // small ship w/ no adjacent direction
for (var j = 0; j < dirs.length; j++) {
var offset = self.vadd(x, y, dirs[j].x, dirs[j].y);
if (board[self.vflat(offset)] === '') {
self.set_in_board(board, offset.x, offset.y, 'w');
//board.splice(i+dirs[j], 1, 'w');
}
}
return;
}
for (var i = 0; self.vflat(self.vadd(x, y, i*dir.x, i*dir.y)) >= 0 && self.vflat(self.vadd(x, y, i*dir.x, i*dir.y)) < 64; i++) {
var center = self.vadd(x, y, i*dir.x, i*dir.y);
if (board[self.vflat(center)] !== 'h') {
break;
}
for (var j = 0; j < dirs.length; j++) {
var offset = self.vadd(center.x, center.y, dirs[j].x, dirs[j].y);
if (board[self.vflat(offset)] === '') {
self.set_in_board(board, offset.x, offset.y, 'w');
//board.splice(i+dirs[j], 1, 'w');
}
}
}
for (var i = 0; self.vflat(self.vadd(x, y, -1*i*dir.x, -1*i*dir.y)) >= 0 && self.vflat(self.vadd(x, y, -1*i*dir.x, -1*i*dir.y)) < 64; i++) {
var center = self.vadd(x, y, -1*i*dir.x, -1*i*dir.y);
if (board[self.vflat(center)] !== 'h') {
break;
}
for (var j = 0; j < dirs.length; j++) {
var offset = self.vadd(center.x, center.y, dirs[j].x, dirs[j].y);
if (board[self.vflat(offset)] === '') {
self.set_in_board(board, offset.x, offset.y, 'w');
//board.splice(i+dirs[j], 1, 'w');
}
}
}
};
self.vadd = function(x1, y1, x2, y2) {
return {x: (x1+x2), y: (y1+y2)};
}
self.vflat = function(x1, y1) {
if (typeof(x1) === "object") {
return 8*x1.x+x1.y;
}
return 8*x1+y1;
}
self.set_in_board = function(board, x, y, val) {
if (x < 8 && x >= 0 && y < 8 && y >= 0) {
board.splice(8*x+y, 1, val);
}
};
self.board_is_won = function(board) {
for (i = 0; i < board.length; i++) {
if (typeof(board[i]) === "number") {
return false;
}
}
return true; // no numbers means all ships sunk -> won
}
self.own_board = function() {
if (self.vue === undefined) { return []; }
if (self.player_1 === self.my_identity) {
return self.vue.board_1;
}
else {
return self.vue.board_2;
}
};
self.opponent_board = function() {
if (self.vue === undefined) { return []; }
if (self.player_1 === self.my_identity) {
return self.vue.board_2;
}
else {
return self.vue.board_1;
}
};
self.new_game = function() {
// Set up a new game
if (self.vue.win_line === "") {
return; // game is not over
}
else {
self.vue.status_line = "Starting a new game";
self.vue.win_line = "";
// Set boards accordingly
if (self.player_1 === self.my_identity) {
self.vue.board_1 = getBoard();
self.vue.board_2 = self.null_board();
}
else {
self.vue.board_2 = getBoard();
self.vue.board_1 = self.null_board();
}
// Set metadata
self.vue.turn_count = 0;
self.vue.game_count++;
self.vue.is_my_turn = false;
self.send_state();
}
};
self.vue = new Vue({
el: "#vue-div",
delimiters: ['${', '}'],
unsafeDelimiters: ['!{', '}'],
data: {
magic_word: "",
chosen_magic_word: null,
need_new_magic_word: false,
my_role: "",
board_1: self.null_board(),
board_2: self.null_board(),
is_other_present: false,
is_my_turn: false,
status_line: "No players present",
win_line: "",
turn_count: 0,
game_count: 0
},
methods: {
set_magic_word: self.set_magic_word,
play: self.play,
new_game: self.new_game,
own_board: self.own_board,
opponent_board: self.opponent_board
}
});
call_server();
return self;
};
var APP = null;
// This will make everything accessible from the js console;
// for instance, self.x above would be accessible as APP.x
jQuery(function(){
APP = app();
APP.initialize();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment