Add practice mode to web client
There is now tons of redundancy between the single player and multiplayers game modes. I just wanted to get this out quickly.
This commit is contained in:
parent
0a1186fa91
commit
b8db367e3f
@ -10,6 +10,10 @@ public class StompTopics {
|
|||||||
|
|
||||||
public static final String PLAYER_REPORTS = createTopicName("playerReports");
|
public static final String PLAYER_REPORTS = createTopicName("playerReports");
|
||||||
|
|
||||||
|
public static final String PRACTICE_GAME = createTopicName("practiceGame");
|
||||||
|
|
||||||
|
public static final String PRACTICE_REPORTS = createTopicName("practiceReports");
|
||||||
|
|
||||||
private static String createTopicName(String suffix) {
|
private static String createTopicName(String suffix) {
|
||||||
return "/topic/lingo/" + suffix;
|
return "/topic/lingo/" + suffix;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,8 @@ public class LingoController implements ApplicationListener<AbstractSubProtocolE
|
|||||||
|
|
||||||
private final Map<String, Game> gameBySession = new HashMap<>();
|
private final Map<String, Game> gameBySession = new HashMap<>();
|
||||||
|
|
||||||
|
private final Map<String, Game> practiceBySession = new HashMap<>();
|
||||||
|
|
||||||
private final Map<String, String> usernameBySession = new HashMap<>();
|
private final Map<String, String> usernameBySession = new HashMap<>();
|
||||||
|
|
||||||
@MessageMapping("/guess")
|
@MessageMapping("/guess")
|
||||||
@ -138,6 +140,39 @@ public class LingoController implements ApplicationListener<AbstractSubProtocolE
|
|||||||
new Thread(new WaitingListListener()).start();
|
new Thread(new WaitingListListener()).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MessageMapping("/practiceGame")
|
||||||
|
public void practiceGame(@Header(SESSION_ID_HEADER) String sessionId) {
|
||||||
|
log.info("Player wants a practice session: {}", sessionId);
|
||||||
|
final Game game = new Game(sessionId, null, wordRepo.getWords(), wordRepo.getGuesses());
|
||||||
|
practiceBySession.put(sessionId, game);
|
||||||
|
final String firstWord = game.newGame();
|
||||||
|
final String firstLetter = String.valueOf(firstWord.charAt(0));
|
||||||
|
log.info("First word: {}", firstWord);
|
||||||
|
sendToUser(sessionId, StompTopics.PRACTICE_GAME, firstLetter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MessageMapping("/practiceGuess")
|
||||||
|
public void practiceGuess(String guess, @Header(SESSION_ID_HEADER) String sessionId) {
|
||||||
|
guess = guess.toUpperCase();
|
||||||
|
log.info("Player {} guessed: {}", sessionId, guess);
|
||||||
|
final Game game = practiceBySession.get(sessionId);
|
||||||
|
final int[] result = game.evaluate(guess);
|
||||||
|
|
||||||
|
// Generate report
|
||||||
|
final Report report = new Report();
|
||||||
|
report.setGuess(guess);
|
||||||
|
if (Game.isCorrect(result)) {
|
||||||
|
final String newWord = game.newWord();
|
||||||
|
final String firstLetter = String.valueOf(newWord.charAt(0));
|
||||||
|
log.info("New word: {}", newWord);
|
||||||
|
report.setCorrect(true);
|
||||||
|
report.setFirstLetter(firstLetter);
|
||||||
|
} else {
|
||||||
|
report.setResult(result);
|
||||||
|
}
|
||||||
|
sendToUser(sessionId, StompTopics.PRACTICE_REPORTS, report);
|
||||||
|
}
|
||||||
|
|
||||||
private void sendToUser(String user, String destination, Object payload) {
|
private void sendToUser(String user, String destination, Object payload) {
|
||||||
// TODO: cache the headers?
|
// TODO: cache the headers?
|
||||||
final SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
|
final SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
|
||||||
|
@ -12,8 +12,9 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2>Web Client (easier)</h2>
|
<h2>Web Client (easier)</h2>
|
||||||
<p>Play the game <a href="client.html">online</a>.</p>
|
<p><a href="practice.html">Practice</a> the game or
|
||||||
<a href="client.html">
|
<a href="multiplayer.html">play online</a>.</p>
|
||||||
|
<a href="multiplayer.html">
|
||||||
<img src="screenshots/lingo-web.png" />
|
<img src="screenshots/lingo-web.png" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Lingo</title>
|
<title>Lingo</title>
|
||||||
<link rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css">
|
<link rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="client.css">
|
<link rel="stylesheet" href="multiplayer.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="usernameDiv" class="container" style="padding-top: 20px">
|
<div id="usernameDiv" class="container" style="padding-top: 20px">
|
||||||
@ -60,6 +60,6 @@
|
|||||||
|
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.1/sockjs.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.1/sockjs.min.js"></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
|
||||||
<script src="client.js"></script>
|
<script src="multiplayer.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
4
server/src/main/resources/static/practice.css
Normal file
4
server/src/main/resources/static/practice.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
15
server/src/main/resources/static/practice.html
Normal file
15
server/src/main/resources/static/practice.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Lingo</title>
|
||||||
|
<link rel="stylesheet" href="practice.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas" width="300" height="400"></canvas>
|
||||||
|
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.1/sockjs.min.js"></script>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
|
||||||
|
<script src="practice.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
266
server/src/main/resources/static/practice.js
Normal file
266
server/src/main/resources/static/practice.js
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
var HEIGHT = 300;
|
||||||
|
var WIDTH = 250;
|
||||||
|
var SIDE = 50;
|
||||||
|
var MARGIN_TOP = 50;
|
||||||
|
var MARGIN_BOTTOM = 25;
|
||||||
|
|
||||||
|
var myScore = 0;
|
||||||
|
var myGuess;
|
||||||
|
var myGuesses;
|
||||||
|
var myProgress;
|
||||||
|
var myResults;
|
||||||
|
var lastWord;
|
||||||
|
|
||||||
|
var canvas = document.getElementById('canvas');
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var client;
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
ctx.font = '25px Monospace';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
|
||||||
|
addKeydownListener();
|
||||||
|
addKeypressListener();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
repaint();
|
||||||
|
|
||||||
|
client = Stomp.over(new SockJS('/stomp'));
|
||||||
|
|
||||||
|
client.connect({}, function(frame) {
|
||||||
|
subscribeToPracticeGame();
|
||||||
|
subscribeToPracticeReports();
|
||||||
|
client.send('/app/lingo/practiceGame');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// special keys
|
||||||
|
function addKeydownListener() {
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
// backspace
|
||||||
|
if (e.which === 8) {
|
||||||
|
myGuess = myGuess.substr(0, myGuess.length - 1);
|
||||||
|
repaint();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
// return
|
||||||
|
else if (e.which === 13) {
|
||||||
|
if (myGuess.length === 5) {
|
||||||
|
client.send("/app/lingo/practiceGuess", {}, myGuess);
|
||||||
|
myGuess = '';
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// characters
|
||||||
|
function addKeypressListener() {
|
||||||
|
document.addEventListener('keypress', function(e) {
|
||||||
|
var charCode = e.charCode;
|
||||||
|
if (isCharacter(charCode)) {
|
||||||
|
if (isCharacterLowercase(charCode)) {
|
||||||
|
charCode = charCode - 32;
|
||||||
|
}
|
||||||
|
var char = String.fromCharCode(charCode);
|
||||||
|
if (myGuess.length < 5) {
|
||||||
|
myGuess += char;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawMyBoard() {
|
||||||
|
var x = 25, y = MARGIN_TOP;
|
||||||
|
drawScore(x, y, myScore);
|
||||||
|
drawInput(x, y, myGuess);
|
||||||
|
var yStart = drawGuesses(x, y, myGuesses, myResults);
|
||||||
|
drawHint(x, yStart, myProgress);
|
||||||
|
drawGrid(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLastWord() {
|
||||||
|
if (lastWord) {
|
||||||
|
var x = canvas.width / 2;
|
||||||
|
var y = canvas.height - MARGIN_BOTTOM / 2;
|
||||||
|
ctx.fillStyle = 'black';
|
||||||
|
ctx.fillText(lastWord.toUpperCase(), x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawScore(x, y, score) {
|
||||||
|
var scoreX = x + WIDTH / 2;
|
||||||
|
var scoreY = y - 25;
|
||||||
|
ctx.fillStyle = 'black';
|
||||||
|
ctx.fillText(score, scoreX, scoreY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGrid(xOrigin, yOrigin) {
|
||||||
|
ctx.beginPath();
|
||||||
|
for (var x = 0; x <= WIDTH; x += SIDE) {
|
||||||
|
ctx.moveTo(xOrigin + x, yOrigin);
|
||||||
|
ctx.lineTo(xOrigin + x, yOrigin + HEIGHT);
|
||||||
|
}
|
||||||
|
for (var y = 0; y <= HEIGHT; y += SIDE) {
|
||||||
|
ctx.moveTo(xOrigin, yOrigin + y);
|
||||||
|
ctx.lineTo(xOrigin + WIDTH, yOrigin + y);
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = 'black';
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInput(xOrigin, yOrigin, input) {
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
var x = xOrigin + SIDE * 0.5;
|
||||||
|
var y = yOrigin + SIDE * 0.5;
|
||||||
|
for (var i = 0; i < myGuess.length; i++) {
|
||||||
|
ctx.fillText(myGuess[i], x, y);
|
||||||
|
x += SIDE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGuesses(xOrigin, yOrigin, guesses, results) {
|
||||||
|
var y = yOrigin + SIDE * 1.5;
|
||||||
|
var numGuesses = Math.min(4, guesses.length);
|
||||||
|
for (var i = 0; i < numGuesses; i++) {
|
||||||
|
var x = xOrigin + SIDE * 0.5;
|
||||||
|
var guess = guesses[guesses.length - numGuesses + i];
|
||||||
|
var result = results[results.length - numGuesses + i];
|
||||||
|
for (var j = 0; j < 5; j++) {
|
||||||
|
if (result[j] === 1) {
|
||||||
|
ctx.fillStyle = 'yellow';
|
||||||
|
ctx.fillRect(x - SIDE * 0.5, y - SIDE * 0.5, SIDE, SIDE);
|
||||||
|
} else if (result[j] === 2) {
|
||||||
|
ctx.fillStyle = 'orange';
|
||||||
|
ctx.fillRect(x - SIDE * 0.5, y - SIDE * 0.5, SIDE, SIDE);
|
||||||
|
}
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
ctx.fillText(guess[j], x, y);
|
||||||
|
x += SIDE;
|
||||||
|
}
|
||||||
|
y += SIDE;
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawResults(xOrigin, yOrigin, results) {
|
||||||
|
var y = yOrigin + SIDE * 1.5;
|
||||||
|
var numResults = Math.min(4, results.length);
|
||||||
|
for (var i = 0; i < numResults; i++) {
|
||||||
|
var x = xOrigin + SIDE * 0.5;
|
||||||
|
var result = results[results.length - numResults + i];
|
||||||
|
for (var j = 0; j < 5; j++) {
|
||||||
|
if (result[j] === 1) {
|
||||||
|
ctx.fillStyle = 'yellow';
|
||||||
|
ctx.fillRect(x - SIDE * 0.5, y - SIDE * 0.5, SIDE, SIDE);
|
||||||
|
} else if (result[j] === 2) {
|
||||||
|
ctx.fillStyle = 'orange';
|
||||||
|
ctx.fillRect(x - SIDE * 0.5, y - SIDE * 0.5, SIDE, SIDE);
|
||||||
|
}
|
||||||
|
x += SIDE;
|
||||||
|
}
|
||||||
|
y += SIDE;
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHint(xOrigin, yOrigin, progress) {
|
||||||
|
var x = xOrigin + SIDE * 0.5;
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
ctx.fillText(progress[i], x, yOrigin);
|
||||||
|
x += SIDE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCharacter(charCode) {
|
||||||
|
return isCharacterLowercase(charCode) || isCharacterUppercase(charCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCharacterLowercase(charCode) {
|
||||||
|
return charCode >= 97 && charCode <= 122;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCharacterUppercase(charCode) {
|
||||||
|
return charCode >= 65 && charCode <= 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidResult(result) {
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
if (result[i] !== 0 && result[i] !== 1 && result[i] !== 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repaint() {
|
||||||
|
// clear the canvas
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// draw the components
|
||||||
|
drawMyBoard();
|
||||||
|
drawLastWord();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(firstLetter, clearScore) {
|
||||||
|
if (!firstLetter) {
|
||||||
|
firstLetter = '';
|
||||||
|
}
|
||||||
|
myGuess = '';
|
||||||
|
myGuesses = [];
|
||||||
|
myProgress = [firstLetter, '', '', '', ''];
|
||||||
|
myResults = [];
|
||||||
|
if (clearScore) {
|
||||||
|
myScore = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribeToPracticeGame() {
|
||||||
|
client.subscribe('/user/topic/lingo/practiceGame', function(message) {
|
||||||
|
var firstLetter = message.body;
|
||||||
|
reset(firstLetter, true);
|
||||||
|
repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribeToPracticeReports() {
|
||||||
|
client.subscribe('/user/topic/lingo/practiceReports', function(message) {
|
||||||
|
var report = JSON.parse(message.body);
|
||||||
|
console.log('My report: ' + report);
|
||||||
|
if (report.correct === true) {
|
||||||
|
var guess = report.guess;
|
||||||
|
var firstLetter = report.firstLetter;
|
||||||
|
console.log('I guessed correctly!');
|
||||||
|
myScore = myScore + 100;
|
||||||
|
lastWord = guess;
|
||||||
|
reset(firstLetter, false);
|
||||||
|
repaint();
|
||||||
|
} else {
|
||||||
|
var guess = report.guess;
|
||||||
|
var result = report.result;
|
||||||
|
console.log('My result: ' + result);
|
||||||
|
// TODO: use isValidResult function
|
||||||
|
if (result[0] === 9) {
|
||||||
|
myGuesses.push('-----');
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
if (result[i] === 2) {
|
||||||
|
myProgress[i] = guess[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myGuesses.push(guess);
|
||||||
|
}
|
||||||
|
myResults.push(result);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
Loading…
x
Reference in New Issue
Block a user