Jump To …

pyramid.coffee

@copy 2012 - Thom Porter (www.thomporter.com)

Card Class

class Card

  constructor: (card) ->

store the card

    @card = card

get the card number

    @num = parseInt(card)

object to hold arrays for click, over & out handlers

    @handlers =
      'click': []
      'over': []
      'out': []

build the card

    @el = $('<div class="card card-' + card + '"></div>');

bind handlers

    @el.on('click', @click)
    @el.on('mouseover', @over)
    @el.on('mouseout', @out)

over handler

  over: =>
    for i in [@handlers.over.length..1] by -1
      @handlers.over[i-1](@) unless typeof @handlers.over[i-1] != 'function'

out handler

  out: =>
    for i in [@handlers.out.length..1] by -1
      @handlers.out[i-1](@) unless typeof @handlers.out[i-1] != 'function'

click handler

  click: =>
    for i in [@handlers.click.length..1] by -1
      @handlers.click[i-1](@) unless typeof @handlers.click[i-1] != 'function'

method for adding click handlers to the card

  addClickHandler: (handler) ->
    @handlers.click.push(handler)

method for adding hover handlers (over/out) to cards

  addHoverHandler: (over,out) ->
    @handlers.over.push(over)
    @handlers.out.push(out)

Deck Class

Controls a deck of cards, pass array of cards to build a custom deck.
Example building a deck of just the 4 aces:
myDeck = new Deck(Array('1h','1d','1s','1c'))

class Deck
  
  constructor: ->
    @cards = [
      '1h','2h','3h','4h','5h','6h','7h','8h','9h','10h','11h','12h','13h',
      '1c','2c','3c','4c','5c','6c','7c','8c','9c','10c','11c','12c','13c',
      '1d','2d','3d','4d','5d','6d','7d','8d','9d','10d','11d','12d','13d',
      '1s','2s','3s','4s','5s','6s','7s','8s','9s','10s','11s','12s','13s',
    ]
    if (arguments.length)
      options = arguments[0]
      @cards = options.cards?

    @deck = @getCards()
    @delt = []

  getCards: ->
    return @cards.slice(0)

  draw: (howMany) ->
    cards = []
    for i in [howMany..1] by 1
      ci = Math.floor(Math.random()*@deck.length)
      cards.push(new Card(@deck.splice(ci,1)[0]));
    return cards[0] unless howMany>1
    return cards

  cardsLeft: ->
    return @deck.length

Game Class

class PyramidSolitaire extends Game
  
  constructor: ->
    @g = $('#game')
    @name = 'Pyramid Solitaire'
    @game_key = 'sol.pyramid'
    super
    @deck = new Deck()
    @currentDiscard = false
    @removed = []
    @removed[0] = 0
    @score = 0
    @snd =
      card: new Audio('/lib/games/snd/card-dealt.mp3')
      deal: new Audio('/lib/games/snd/shuffle-short.mp3')
    @snd.deal.volume = 0.25
    @snd.card.volume = 0.5
    @deal();


  newGame: =>
    super
    @deck = new Deck()
    @currentDiscard = false
    @removed = []
    @removed[0] = 0
    @score = 0
    @deal();


  append: (el) ->
    el.appendTo(@g);



  deal: ->
    @snd.deal.play();
    @g.html('');
    @buildUI()
    @field = $('<div class="play-field"></div>').appendTo(@g);

    row = 0
    col = 0
    @cards = []
    while (++row < 8)
      col = 0
      @removed[row] = []
      @cards[row] = []
      while (col++<row)
        @removed[row][col] = 0
        @cards[row][col] = @deck.draw(1)
        @cards[row][col].el.appendTo(@field)
        @cards[row][col].p = 'c' + row + '-' + col
        @cards[row][col].el.addClass(@cards[row][col].p)
        @cards[row][col].removed = false
        @cards[row][col].addClickHandler(@cardClick)
        @cards[row][col].addHoverHandler(@cardOver,@cardOut)


    @pile = $('<div class="card pile"></div>').appendTo(@g).on('click',@pileClick)
    @cardsLeft = $('<span class="cardsleft vert"></span>').appendTo(@pile)
    @cardsLeft.text(@deck.cardsLeft())
    @discard = $('<div class="card discard"></div>').appendTo(@g)
    @_drawFromPile()

method to check and see if the game is over

  isOver: ->
    row = 0
    col = 0
    allRemoved = true
    while (++row < 8)
      col = 0
      while (col++<row)
        allRemoved = false unless @removed[row][col]

return 2 when they actually win the game...

    return 2 unless !allRemoved

now see if the pile in the deck is empty

    @deck.deck.length == 0 && !@hasMove()

  hasMove: ->
    hasMove = false
    row = 0
    col = 0
    while (++row < 8)
      col = 0
      while (col++<row)
        c = @cards[row][col]
        if !c.removed
          if @_canClick(c) && (c.num+1 == @currentDiscard.num || c.num-1 == @currentDiscard.num || (@currentDiscard.num == 1 && c.num==13) || (@currentDiscard.num == 13 && c.num == 1))
            hasMove = true
    hasMove





  cardOver: (c) =>
    c.el.addClass('overcard') unless !@_canClick(c)

  cardOut: (c) ->
    c.el.removeClass('overcard');

  cardClick: (c) =>
    if @_canClick(c) && (c.num+1 == @currentDiscard.num || c.num-1 == @currentDiscard.num || (@currentDiscard.num == 1 && c.num==13) || (@currentDiscard.num == 13 && c.num == 1))
      @snd.card.play()
      @updateScore(5)
      p = @_getRowCol(c.p)
      @removed[p[0]][p[1]] = 1
      @discard.removeClass('card-'+@currentDiscard.card).addClass('card-'+c.card)
      @currentDiscard = c;
      @current = c.num
      c.el.remove()
      c.removed = true
      @checkGameState()

  checkGameState: ->
    io = @isOver()
    if io == 2
      @win()
    else if io
      @gameOver()

    if io
      @pile.off('click')


  gameOver: ->
    super
    alert('Game Over - Final Score: ' + @score)

  win: ->
    @hi.addScore(@score) if @includeHiScores
    alert('You Won!\nFinal Score: ' + @score);

  pileClick: =>
    left = @deck.cardsLeft()
    if left > 0
      @_drawFromPile()
      @cardsLeft.text(left-1)
      @snd.card.play()
    @checkGameState()

  updateScore: ->
    if (arguments[0]?)
      @score += arguments[0]
    @scoreView.text(@score)

  _drawFromPile: ->
    if (@currentDiscard != false)
      @discard.removeClass('card-'+@currentDiscard.card)
    @currentDiscard = @deck.draw(1)
    @discard.addClass('card-'+@currentDiscard.card)


  _canClick: (c) ->
    p = @_getRowCol(c.p)
    if (p[0] == 7)
      return true
    (@removed[p[0]+1][p[1]] && @removed[p[0]+1][p[1]+1])

  _getRowCol: (p) ->
    pp = p.substr(1).split('-'); # p[0] = row, p[1] = column
    pp[0] = parseInt(pp[0])
    pp[1] = parseInt(pp[1])
    pp


$ ->
  $.g = new PyramidSolitaire