Skip to content

BW-JC Share Game based on URL #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

[MathSwipe](http://sf-interns.github.io/mathswipe/) is a `CoffeeScript` game developed in the context of the [Originate](http://www.originate.com/) 'Intern Hack 2015' Event, presented August 2015. It is a web-game in which the user is given a grid of numbers (0-9) and operators (+, -, ×). The user's goal is to swipe a path through the cells to create an expression that evaluates to one of the goal-values listed below the board. A level is completed when the user finds expressions evaluating to each goal-value and has cleared the board of all cells.

Math Swipe is a simple brain game that is rooted in math. From children to their parents, Math Swipe is simple yet challenging enough to stump even the strongest math wizzes.

## Development Information

Note that the other services, when run locally, expect the frontend to be served on port 8080.

The codebase is primarily written in CoffeeScript using the Node package manager to install most of the dependencies. The entire project (including its dependencies) is bundled by `Webpack` into a single bundle.js file which can be served locally or staticlly with GitHub pages.

Animation and functionality of the game is implemented using `jQuery`, `Two.js` and Scalable Vector Graphics (SVGs).
Animation and functionality of the game is implemented using `jQuery`, `Two.js` and Scalable Vector Graphics (SVGs).

The styling is written in `SASS` and compiled down to CSS before use. If you plan to clone this repository and try building the game locally, we recommend using the Sublime plugin `SASS Build` to compile down to CSS.

Expand Down
59 changes: 35 additions & 24 deletions app/controllers/MathSwipeController.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ InputSolver = require '../services/InputSolver'
RandomizedFitLength = require '../services/RandomizedFitLength'
ResetButton = require '../services/ResetButton'
RunningSum = require '../services/RunningSum'
ShareGameService = require '../services/ShareGameService'
SolutionService = require '../services/SolutionService'
Title = require '../services/Title'
TrackingService = require '../services/TrackingService'
Expand All @@ -23,7 +24,6 @@ class MathSwipeController
constructor: ->
@gameScene = @createGameScene()
@symbols = @getSymbols()
@initialize()
@bindNewGameButton()
HowToPlay.createHowToPlay @isMobile
if @isMobile().any()?
Expand All @@ -32,30 +32,38 @@ class MathSwipeController
else
TrackingService.desktopView()
@cursorToPointer()
ShareGameService.setMessage()
@initialize window.location.hash

# # Uncomment the following line to perform general tests
# GeneralTests.tests @board

initialize: ->
length = 3
inputs = []
answers = []

inputLengths = RandomizedFitLength.generate length * length

@generateInputs inputLengths, inputs, answers

console.log expression for expression in inputs
console.log '\n'

gameModel = @generateBoard inputs, length
@goalContainer = new GoalContainer answers, Colors
@board = new Board gameModel, @gameScene, answers, @symbols,
initialize: (hash) ->
solutionPlacements = []
goals = []
boardValues = []
hasCompleteBoard = false
if hash? and hash isnt ''
hasCompleteBoard = ShareGameService.decode boardValues, goals, solutionPlacements
unless hasCompleteBoard
length = 3
goals = []
solutionPlacements = []
inputs = []
inputLengths = RandomizedFitLength.generate length * length
@generateInputs inputLengths, inputs, goals
boardValues = @generateBoard inputs, length, solutionPlacements

@goalContainer = new GoalContainer goals, Colors
@board = new Board boardValues, @gameScene, goals, @symbols,
@goalContainer, @isMobile().any()?, Cell,
Colors, ClickHandler, SolutionService,
BoardSolvedService, RunningSum

ResetButton.bindClick @board, RunningSum
RunningSum.empty()
@createNewGame() unless ShareGameService.reloadPageWithHash(@board,
solutionPlacements, SolutionService)

isMobile: () ->
Android: () ->
Expand All @@ -76,10 +84,13 @@ class MathSwipeController
bindNewGameButton: ->
$('#new-game-button').click (e) =>
TrackingService.boardEvent 'new game'
@gameScene.clear()
@goalContainer.clearGoals()
ResetButton.unbindClick()
@initialize()
@createNewGame()

createNewGame: ->
@gameScene.clear()
@goalContainer.clearGoals()
ResetButton.unbindClick()
@initialize (window.location.hash = '')

createGameScene: ->
gameDom = document.getElementById('game')
Expand Down Expand Up @@ -110,16 +121,16 @@ class MathSwipeController

# -------- Back-end -------- #

generateBoard: (inputs, length) ->
DFS.setEquationsOnGrid length, inputs, AdjacentCellsCalculator
generateBoard: (inputs, length, solutionPlacements) ->
DFS.setEquationsOnGrid length, inputs, AdjacentCellsCalculator, solutionPlacements

generateInputs: (inputLengths, inputs, answers) ->
generateInputs: (inputLengths, inputs, goals) ->
for inputSize in inputLengths
value = -1
while value < 1 or value > 300
expression = ExpressionGenerator.generate inputSize
value = InputSolver.compute expression
answers.push (InputSolver.compute expression)
goals.push (InputSolver.compute expression)
inputs.push expression.split('')

randExpression: (length) ->
Expand Down
17 changes: 13 additions & 4 deletions app/services/DFS.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ Tuple = require '../models/Tuple'

class DFS

@setEquationsOnGrid: (@size, inputList, @AdjacentCells) ->
@setEquationsOnGrid: (@size, inputList, @AdjacentCells, solutionPlacements) ->
@clearSolutionGrid()
grid = @createEmptyGrid()
for i in [0...10000]
if @hasFoundSolution inputList
index = 0
for input in inputList
placementList = []
for idx in [0...input.length]
placementList.push @solutionPlacements[index++]
solutionPlacements.push placementList
for row in [[email protected]]
for col in [[email protected]]
grid[@solutionGrid[row][col].y][@solutionGrid[row][col].x] = @solutionGrid[row][col].value
Expand All @@ -23,14 +29,15 @@ class DFS
@solutionGrid[row].push (new GridCell col, row)

@hasFoundSolution: (inputList) ->
@solutionPlacements = []
for i in [0...inputList.length]
hasPlaced = false
for index in [0...20]
unless hasPlaced
cloneGrid = @cloneSolutionGrid()
seedX = Math.floor(Math.random() * @size)
seedY = Math.floor(Math.random() * @size)
if @search seedX, seedY, inputList[i]
if @search seedX, seedY, inputList[i], @solutionPlacements
hasPlaced = true
else
@solutionGrid = cloneGrid
Expand All @@ -39,7 +46,7 @@ class DFS
else return false
true

@search: (seedX, seedY, input) ->
@search: (seedX, seedY, input, @solutionPlacements) ->
return true if input.length is 0

toVisit = @shuffle @AdjacentCells.getAdjacent @solutionGrid, seedX, seedY
Expand All @@ -48,8 +55,10 @@ class DFS
curr = toVisit.pop()
while curr != undefined
@solutionGrid[curr.y][curr.x].value = input[0]
unless @search curr.x, curr.y, input.slice(1, input.length)
@solutionPlacements.push [curr.y, curr.x]
unless @search curr.x, curr.y, input.slice(1, input.length), @solutionPlacements
@solutionGrid[curr.y][curr.x].value = ' '
@solutionPlacements.pop()
curr = toVisit.pop()
else return true
false
Expand Down
124 changes: 124 additions & 0 deletions app/services/ShareGameService.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
$ = require 'jquery'

class ShareGameService

@reloadPageWithHash: (board, solutionPlacements, SolutionService) ->
unless @checkSolutionPlacements board, solutionPlacements, SolutionService
window.location.hash = ''
return false
hash = @encode board.initialValues, board.goals, solutionPlacements
window.location.hash = hash

@encode: (boardValues, goals, solutionPlacements) ->
boardValues = (JSON.stringify boardValues).replace(/(\[|\]|"|,|{|})*/g, '')

length = Math.sqrt boardValues.length
for list in [0...solutionPlacements.length]
for pos in [0...solutionPlacements[list].length]
solutionPlacements[list][pos] = solutionPlacements[list][pos][0] * length +
solutionPlacements[list][pos][1]

btoa(JSON.stringify {b: boardValues, g: goals, p: solutionPlacements})

@decode: (boardValues, goals, solutionPlacements) ->
try
decoded = atob window.location.hash.substr(1, window.location.hash.length)
decoded = JSON.parse decoded
catch e
decoded = null
return false unless decoded? and decoded.b? and decoded.g? and
decoded.p? and @isValidDecode decoded

length = Math.sqrt decoded.b.length
@decodeBoardValues decoded.b, boardValues, length
@decodeGoals decoded.g, goals
@decodeSolutionPlacements decoded.p, solutionPlacements, length
true

@isValidDecode: (decoded) ->
alphabet = ['"', '{', '}', '[', ']', ',', ':',
'b', 'g', 'p', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '0',
'+', '-', '*']
for char in decoded
return false if alphabet.indexOf(char) is -1
true

@decodeBoardValues: (copy, boardValues, length) ->
index = 0
for i in [0...length]
row = []
for j in [0...length]
row.push copy[index++]
boardValues.push row

@decodeGoals: (copy, goals) ->
goals.push goal for goal in copy

@decodeSolutionPlacements: (copy, solutionPlacements, length) ->
for list in [0...copy.length]
expression = []
for coord in [0...copy[list].length]
expression.push [(Math.floor copy[list][coord] / length),
(copy[list][coord] % length)]
solutionPlacements.push expression

@checkSolutionPlacements: (board, solutionPlacements, SolutionService) ->
@initializeTempBoard board
@solutionService = new SolutionService @tempBoard, board.goals

inputs = []
for expression in solutionPlacements
clickedCells = []
for index in [0...expression.length]
cell = expression[index]
clickedCells.push {row: cell[0], col: cell[1]}
@solutionService.initialize clickedCells

solution = []
for cell in clickedCells
solution.push @tempBoard.boardValues[cell.row][cell.col]
@tempBoard.boardValues[cell.row][cell.col] = ' '
@pushDownTempBoard()

unless @solutionService.isSolution()
return false
inputs.push solution

console.log expression for expression in inputs
console.log '\n'
true

@initializeTempBoard: (board) ->
@tempBoard = {}
@tempBoard.boardValues = []
for row, i in board.initialValues
@tempBoard.boardValues.push []
for col in row
@tempBoard.boardValues[i].push col

@pushDownTempBoard: ->
for row in [@tempBoard.boardValues.length-1..1]
for col in [@tempBoard.boardValues.length-1..0]
if @tempBoard.boardValues[row][col] is ' '
for up in [row-1..0]
unless @tempBoard.boardValues[up][col] is ' '
@swapCells row, col, up, col
break

@swapCells: (r1, c1, r2, c2) ->
temp = @tempBoard.boardValues[r1][c1]
@tempBoard.boardValues[r1][c1] = @tempBoard.boardValues[r2][c2]
@tempBoard.boardValues[r2][c2] = temp

@setMessage: ->
possible = ['Play MathSwipe with me! Try to beat my score at',
'Play MathSwipe with me! Try to solve my board at',
'Play MathSwipe with me! Solve my puzzle at']
text = possible[Math.floor(Math.random() * 3)]
$( '#tweet' ).attr( 'data-text' , text )
console.log $('#fb-share')
$( '#fb-share' ).attr( 'data-href' , window.location.hash )


module.exports = ShareGameService
Loading