Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
71 changes: 71 additions & 0 deletions game.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const fs = require('fs');

const scoreByIndex = [30, 20, 20, 10, 10, 10];
const filePath = './scores.json';

const hitTarget = (hits, target) => {
switch (target) {
case 1:
hits = hits.map((canState, index) => canState || index === 0);
break;
case 2:
hits = hits.map((canState, index) => canState || index < 2);
break;
case 3:
hits = hits.map((canState, index) => canState || [0, 2].includes(index));
break;
case 4:
hits = hits.map((canState, index) => canState || [0, 1, 3].includes(index));
break;
case 5:
hits = hits.map(() => true);
break;
case 6:
hits = hits.map((canState, index) => canState || [0, 2, 5].includes(index));
}
return hits;
};

const calculateScore = hits => hits.reduce((score, canState, index) => score += canState && scoreByIndex[index], 0);

const retrieveScores = (callback) => {
fs.readFile(filePath, (err, data) => {
callback(data, err);
});
};

const saveScore = (username, score) => {
retrieveScores((data, err) => {
// if file does not exist yet
if (err && err.code === "ENOENT") {
return fs.writeFile(filePath, JSON.stringify([{ username, score }]), error => console.error);
}
else if (err) {
console.error(err);
}
else {
try {
let scores = JSON.parse(data);
const oldScore = scores.find(scoreLine => scoreLine.username === username);

if (!oldScore || oldScore.score < score) {
scores = [
...scores.filter(scoreLine => scoreLine.username !== username),
{ username, score },
];
}

return fs.writeFile(filePath, JSON.stringify(scores), error => console.error)
} catch (exception) {
console.error(exception);
}
}
});
}

module.exports = {
hitTarget,
calculateScore,
retrieveScores,
saveScore,
}
102 changes: 102 additions & 0 deletions game.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const { hitTarget, calculateScore } = require('./game');

test('it should hit target 1', () => {
const hits = [false, false, false, false, false, false];
const target = 1;
const expected = [true, false, false, false, false, false];
expect(hitTarget(hits, target)).toEqual(expected);
});

test('it should hit target 2', () => {
const hits = [false, false, false, false, false, false];
const target = 2;
const expected = [true, true, false, false, false, false];
expect(hitTarget(hits, target)).toEqual(expected);
});

test('it should hit target 3', () => {
const hits = [false, false, false, false, false, false];
const target = 3;
const expected = [true, false, true, false, false, false];
expect(hitTarget(hits, target)).toEqual(expected);
});

test('it should hit target 4', () => {
const hits = [false, false, false, false, false, false];
const target = 4;
const expected = [true, true, false, true, false, false];
expect(hitTarget(hits, target)).toEqual(expected);
});

test('it should hit target 5', () => {
const hits = [false, false, false, false, false, false];
const target = 5;
const expected = [true, true, true, true, true, true];
expect(hitTarget(hits, target)).toEqual(expected);
});

test('it should hit target 6', () => {
const hits = [false, false, false, false, false, false];
const target = 6;
const expected = [true, false, true, false, false, true];
expect(hitTarget(hits, target)).toEqual(expected);
});

test('it should hit two different targets', () => {
let hits = [false, false, false, false, false, false];
const firstTarget = 4;
const secondTarget = 6;
const expected = [true, true, true, true, false, true];

hits = hitTarget(hits, firstTarget);
hits = hitTarget(hits, secondTarget);

expect(hits).toEqual(expected);
});

test('it should hit twice same target', () => {
let hits = [false, false, false, false, false, false];
const target = 1;
const expected = [true, false, false, false, false, false];

hits = hitTarget(hits, target);
hits = hitTarget(hits, target);

expect(hits).toEqual(expected);
});

test('it should calculate score correctly', () => {
const expectations = [
{
hits: [true, false, false, false, false, false],
score: 30,
},
{
hits: [true, true, false, false, false, false],
score: 50,
},
{
hits: [true, false, true, false, false, false],
score: 50,
},
{
hits: [true, true, true, false, false, false],
score: 70,
},
{
hits: [true, true, true, true, false, false],
score: 80,
},
{
hits: [true, true, true, false, false, true],
score: 80,
},
{
hits: [true, true, true, true, true, true],
score: 100,
},
];
expectations.forEach(expectation => {
expect(calculateScore(expectation.hits)).toEqual(expectation.score);
});
});
121 changes: 112 additions & 9 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,43 @@
<head>
<title>Chamboulle tout</title>
<script src="https://unpkg.com/vue"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<script src="/socket.io/socket.io.js"></script>
<style>
.material-icons {
cursor: pointer;
}
</style>
</head>
<body>
<div id="glob" align="center">
<h1>Chamboulle tout!!</h1>

<button class="material-icons" @click="reset()">replay</button>

<button v-if="!displayScores" @click="getHighScores()">show high scores</button>

<div id="scores" v-if="displayScores">
<button @click="displayScores = false">hide scores</button>
</div>

<ul>
<li v-for="{username, score} of scores">{{ username + ':' + score}}</li>
</ul>

<table>
<tr>
<td>

</td>
<td>
<input type="checkbox">
<i v-if="!hits[0]" class="material-icons" @click="throwOn(1)">
looks_one
</i>
<i v-else class="material-icons" @click="throwOn(1)">
texture
</i>
</td>
<td>

Expand All @@ -24,33 +50,110 @@ <h1>Chamboulle tout!!</h1>
<table style="margin-left: 10px">
<tr>
<td>
<input type="checkbox">
<i v-if="!hits[1]" class="material-icons" @click="throwOn(2)">
looks_two
</i>
<i v-else class="material-icons" @click="throwOn(2)">
texture
</i>
</td>
<td>
<input type="checkbox">
<i v-if="!hits[2]" class="material-icons" @click="throwOn(3)">
looks_3
</i>
<i v-else class="material-icons" @click="throwOn(3)">
texture
</i>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<input type="checkbox">
<i v-if="!hits[3]" class="material-icons" @click="throwOn(4)">
looks_4
</i>
<i v-else class="material-icons" @click="throwOn(4)">
texture
</i>
</td>
<td>
<input type="checkbox">
<i v-if="!hits[4]" class="material-icons" @click="throwOn(5)">
looks_5
</i>
<i v-else class="material-icons" @click="throwOn(5)">
texture
</i>
</td>
<td>
<input type="checkbox">
<i v-if="!hits[5]" class="material-icons" @click="throwOn(6)">
looks_6
</i>
<i v-else class="material-icons" @click="throwOn(6)">
texture
</i>
</td>
</tr>
</table>
</div>

<script type="text/javascript">
var chamboulletout = new Vue({
el: '#glob'

const socket = io();
const chamboulletout = new Vue({
el: '#glob',
data: {
remainingHits: 2,
hits: [],
scores: [],
displayScores: false
},
created() {
this.reset()
const username = prompt('What\'s your name?')
socket.emit('set username', username)
},
methods: {
reset () {
socket.emit('reset')
this.remainingHits = 2
this.hits = Array.from(Array(6), () => false)
},
throwOn (target) {
if (!this.remainingHits) {
return
}
this.remainingHits--;
switch(target) {
case 1:
this.hits = this.hits.map((canState, index) => canState || index === 0)
break;
case 2:
this.hits = this.hits.map((canState, index) => canState || index < 2)
break;
case 3:
this.hits = this.hits.map((canState, index) => canState || [0, 2].includes(index))
break;
case 4:
this.hits = this.hits.map((canState, index) => canState || [0, 1, 3].includes(index))
break;
case 5:
this.hits = this.hits.map(() => true)
break;
case 6:
this.hits = this.hits.map((canState, index) => canState || [0, 2, 5].includes(index))
}
socket.emit('hit', target)
},
getHighScores() {
fetch('/highscores')
.then(response => response.json())
.then(scores => {
this.scores = scores
this.displayScores = true
})
}
},
})

</script>
Expand Down
55 changes: 55 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
const { hitTarget, calculateScore, saveScore, retrieveScores } = require('./game');

app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});

app.get('/highscores', (req, res) => {
retrieveScores((scores, err) => {
res.send(err ? [] : JSON.parse(scores));
});
});

io.on('connection', (socket) => {
console.log('a user connected');
let remainingHits = 2;
let score = 0;
let username = 'guest';
let hits = Array.from(Array(6), () => false);

socket.on('set username', name => {
username = name;
});

socket.on('reset', () => {
remainingHits = 2;
score = 0;
hits = Array.from(Array(6), () => false);
});

socket.on('hit', target => {
if (remainingHits) {
remainingHits--;
hits = hitTarget(hits, target);
score = calculateScore(hits);
console.log('you hit '+target);
}
if (!remainingHits || target === 5) {
saveScore(username, score);
remainingHits = 0;
} else {
console.log(remainingHits+' remaining hits');
}
});

socket.on('disconnect', () => {
console.log(username+' disconnected');
});
});

http.listen(3000, () => {
console.log('listening on *:3000');
});
Loading