Skip to content

Instantly share code, notes, and snippets.

@noway
Last active December 14, 2015 19:59
Show Gist options
  • Save noway/5140455 to your computer and use it in GitHub Desktop.
Save noway/5140455 to your computer and use it in GitHub Desktop.
Terminal arkanoid on coffeescript+node.js. Execute `coffee arkanoid.coffee` in terminal to run.
if not process.stdout.isTTY
console.log 'To play game run it in terminal'
return
# terminal props
terminalBuffer = []
terminalSize = [0, 0]
terminalCenter = [0, 0]
# plate props
plateX = 0
plateSize = 5
# global game props
firstTime = true
gameEnd = false
isGameOver = false
startScreen = true
score = 0
level = 1
# ball props
ball = [0, 0]
ballVel = [-1, -1] # horizontal, vertical
ballLaunched = false
# blocks props
blocks = []
blockLeng = 4
blocksOnLine = 0
setSymbol = (x, y, symb = '_') ->
if x > terminalSize[0] or x < 0 or
y > terminalSize[1] or y < 0
console.error x, y
if not terminalBuffer[y]? or not terminalBuffer[y][x]?
console.error x, y
terminalBuffer[y][x] = symb
resetBuffer = ->
terminalBuffer = []
for i in [0...terminalSize[1]]
terminalBuffer[i] = []
for j in [0...terminalSize[0]]
terminalBuffer[i][j] = ' '
return
resetScreen = ->
if firstTime
process.stdout.write "\x1b[?25l" # hide cursor
firstTime = false
return
for i in [0...terminalSize[1]]
if i is terminalSize[1] - 1
process.stdout.write "\x1b[2K\r"
break
process.stdout.write "\x1b[A"
return
render = ->
resetScreen()
for line, i in terminalBuffer
newLine = '\n'
if i is terminalBuffer.length - 1
newLine = ''
process.stdout.write line.join('') + newLine
return
drawRect = (x, y, width, height) ->
for i in [y...y + height]
for j in [x...x + width]
symb = null
if i is y or i is y + height - 1
if j isnt x and j isnt x + width - 1
symb = '\u2550'
else
if j is x or j is x + width - 1
symb = '\u2551'
if i is y and j is x
symb = '\u2554'
else if i is y and j is x + width - 1
symb = '\u2557'
else if i is y + height - 1 and j is x
symb = '\u255A'
else if i is y + height - 1 and j is x + width - 1
symb = '\u255D'
setSymbol j, i, symb if symb isnt null
return
drawPlate = ->
for i in [0...plateSize]
setSymbol plateX + i, terminalSize[1] - 3 , '\u2594'
return
drawText = (x, y, text, align = 0) ->
if align is 1
x -= Math.round text.length / 2
else if align is 2
x -= text.length
for i in [0...text.length]
setSymbol x + i, y, (text.substr i, 1)
return
renderScene = ->
resetBuffer()
drawRect 1, 1, terminalSize[0] - 2, terminalSize[1] - 2
if startScreen
drawText terminalCenter[0], terminalCenter[1] - 3,
"Controls:", 1
drawText terminalCenter[0], terminalCenter[1] - 2,
"A for left, D for right", 1
drawText terminalCenter[0], terminalCenter[1] - 1,
"Space for launch ball", 1
drawText terminalCenter[0], terminalCenter[1] + 1,
"Press anykey to start", 1
drawText terminalCenter[0], terminalCenter[1] + 3,
"Author: noway", 1
render()
return
drawPlate()
for i in [0...blocks.length]
for j in [0...blocks[i].length]
if blocks[i][j] is true
for k in [0...blockLeng]
datSymb = '\u2586'
if k is 0
datSymb = '['
if k is blockLeng - 1
datSymb = ']'
setSymbol (j * blockLeng) + k + 2, i + 2, datSymb
if not ballLaunched
setSymbol ball[0], terminalSize[1] - 4, 'O'
else
setSymbol ball[0], ball[1], 'O'
drawText terminalSize[0] - 1, 0, "Score: " + score, 2
drawText 1, 0, "Level: " + level, 0
if gameEnd
endText = if isGameOver then "GAME OVER" else "NEXT LEVEL"
drawText terminalCenter[0], terminalCenter[1], endText, 1
render()
resetGame = ->
score = 0
halfPlate = plateSize / 2
plateX = Math.round terminalCenter[0] - halfPlate
ball = [Math.round(terminalCenter[0]), terminalSize[1] - 4]
ballVel = [-1, -1]
ballLaunched = false
blocksOnLine = Math.floor (terminalSize[0] - 4) / blockLeng
for i in [0...4]
blocks[i] = []
for j in [0...blocksOnLine]
blocks[i][j] = true
return
endGame = (gameOver = false) ->
gameEnd = true
isGameOver = gameOver
renderScene()
setTimeout ->
if not isGameOver
level++
else level = 0
gameEnd = false
resetGame()
renderScene()
, 500
onResize = ->
terminalSize = [process.stdout.columns, process.stdout.rows]
terminalCenter = [
Math.round((terminalSize[0] - 4) / 2 + 2),
Math.round((terminalSize[1] - 4) / 2 + 2),
]
renderScene()
onKeyPress = (key) ->
if gameEnd
return
if key.toLowerCase() is 'a'
plateX--
ball[0]-- if not ballLaunched
else if key.toLowerCase() is 'd'
plateX++
ball[0]++ if not ballLaunched
else if key is ' '
ball[1] = terminalSize[1] - 4
ballLaunched = true
if plateX > terminalSize[0] - 3 - plateSize + 1
plateX--
ball[0]-- if not ballLaunched
else if plateX < 2
plateX++
ball[0]++ if not ballLaunched
renderScene()
ballPhisics = ->
for i of ball
ball[i] += ballVel[i]
if ball[0] >= terminalSize[0] - 3 then ballVel[0] = -1
if ball[0] <= 2 then ballVel[0] = 1
if ball[1] >= terminalSize[1] - 4 and ballVel[1] is 1
if ball[1] >= terminalSize[1] - 3
endGame(true)
else
if ball[0] == plateX - 1 and ballVel[0] is 1
ballVel[0] = -1
ballVel[1] = -1
else if ball[0] == plateX + plateSize and ballVel[0] is -1
ballVel[0] = 1
ballVel[1] = -1
else if ball[0] >= plateX and ball[0] < plateX + plateSize
ballVel[1] = -1
if ball[1] <= 2 then ballVel[1] = 1
currLine = ball[1] - 2
if currLine <= blocks.length and currLine > 0
currIndex = Math.floor ball[0] / blockLeng
if blocks[currLine - 1][currIndex]
blocks[currLine - 1][currIndex] = false
blocksDestroyed = true
for i in blocks
for j in i
if j
blocksDestroyed = false
break
if not blocksDestroyed
break
score++
ballVel[1] *= -1
process.stdout.write "\x07" # bell
if blocksDestroyed then endGame()
process.stdin.setRawMode true
process.stdin.resume()
process.stdin.setEncoding 'utf8'
process.stdin.on 'data', (key) ->
if startScreen
startScreen = false
resetGame()
renderScene()
if key is 'a' or key is 'd' or key is ' '
onKeyPress key
else if key is '\u0003'
process.stdout.write "\x1b[?25h\n" # show cursor and make new line
process.exit()
process.stdout.on 'resize', onResize
setInterval ->
return if startScreen
return if gameEnd
return if not ballLaunched
ballPhisics()
renderScene()
, 100
onResize()
renderScene()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment