JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
no need for crazy array maniplulation
[hexbog.git] / main.coffee
index 1847e56..89bc695 100644 (file)
@@ -26,6 +26,8 @@ slide_ms = 2000
 
 board_col_heights = [5, 6, 7, 8, 7, 6, 5]
 
+# code and css will need adjusting if you change HP_MAX
+HP_MAX = 10
 
 ##############################################################
 ##############    fix javascript some more    ################
@@ -125,18 +127,19 @@ new_letter_queue = []
 new_letter = ->
        if new_letter_queue.length
                l = new_letter_queue.shift()
-               if l is 'Q'
-                       return 'Qu'
-               else
-                       return l
+               l.letter = l.letter.toUpperCase()
+               if l.letter is 'Q'
+                       l.letter = 'Qu'
+               return l
+       hp = 1 + Math.floor(Math.random() * (HP_MAX - 1))
        r = Math.floor Math.random() * (letter_distribution_total + 1)
        for i in [0..25]
                r -= letter_distribution[i]
                if r <= 0
                        if letters[i] is 'Q'
-                               return 'Qu'
-                       return letters[i]
-       return 'Z' # just in case
+                               return letter: 'Qu', hp: hp
+                       return letter: letters[i], hp: hp
+       return letter: 'Z', hp: hp # just in case
 
 
 
@@ -222,6 +225,48 @@ init_board_layout = () ->
 
                col_offset += board_col_heights[col_num]
 
+# support obsolete save data format
+load_game_0 = (encoded) ->
+       letters = (encoded.substr 0, board_tiles).split ''
+       for l in letters
+               new_letter_queue.push {
+                       letter: l,
+                       hp: 1 + Math.floor(Math.random() * (HP_MAX - 1))
+               }
+       score = parseInt(encoded.substr(board_tiles), 10)
+
+load_game_1 = (encoded) ->
+       int = 0
+       encoded = encoded.substr 1
+       score = parseInt(encoded.substr(board_tiles * 3 / 2), 10)
+       for t in [0...(tiles.length * 3 / 2)] by 3
+               int = 0
+               for d in [0...3]
+                       int *= 44
+                       char = encoded[t + 2 - d]
+                       int += save_charset.indexOf(char)
+               t2hp = int % 11
+               int = Math.floor(int / 11)
+               t2letter = String.fromCharCode(char_a + (int % 26))
+               int = Math.floor(int / 26)
+               t1hp = int % 11
+               int = Math.floor(int / 11)
+               t1letter = String.fromCharCode(char_a + (int % 26))
+               new_letter_queue.push {
+                       letter: t1letter,
+                       hp: t1hp
+               }
+               new_letter_queue.push {
+                       letter: t2letter,
+                       hp: t2hp
+               }
+
+load_game = (encoded) ->
+       switch encoded.substr 0, 1
+               when "1"
+                       load_game_1(encoded)
+               else
+                       load_game_0(encoded)
 
 init_board = ->
        encoded = window.location.hash
@@ -230,8 +275,7 @@ init_board = ->
        unless encoded? and encoded.length > board_tiles
                encoded = get_cookie 'hexbog'
        if encoded? and encoded.length > board_tiles
-               new_letter_queue = (encoded.substr 0, board_tiles).split ''
-               score = parseInt(encoded.substr(board_tiles), 10)
+               load_game encoded
 
        # work out which grid spaces are connected
        # (neighbors, above, down)
@@ -291,37 +335,142 @@ unselect_all = ->
                _unselect_tile()
        update_selection_display()
 
-shrink_selection = (leave_count) ->
-       while selected.length > leave_count
-               _unselect_tile()
-       update_selection_display()
-
 selected_word = ->
        word = ''
        word += tiles[i].text for i in selected
        return word.toLowerCase()
 
+save_charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR'
+char_a = "a".charCodeAt(0)
 save_game = ->
-       encoded = ''
-       for t in tiles
-               encoded += t.text.substr 0, 1
+       encoded = '1' # save format
+       for i in [0...tiles.length] by 2
+               int = tiles[i].text.toLowerCase().charCodeAt(0) - char_a
+               int *= 11
+               int += tiles[i].hp
+               int *= 26
+               int += tiles[i+1].text.toLowerCase().charCodeAt(0) - char_a
+               int *= 11
+               int += tiles[i+1].hp
+               for d in [0...3]
+                       encoded += save_charset.substr(int % 44, 1)
+                       int = Math.floor(int / 44)
        encoded += score
        set_cookie 'hexbog', encoded, 365
        window.location.hash = encoded
 
+unsink = (tile) ->
+       tile.new_hp = 10
+       tile.text = new_letter().letter
+       tile.dom.html tile.text
+
 # remove the selected tiles from the board, create new tiles, and slide everything into place
 blip_selection = ->
+       difficulty = 11 - Math.log(400 + score) # higher numbers are easier
+       force = difficulty * score_for selected_word() # how much tile restoration we have left to do
+       word_length = selected_word().length
        faders = selected.num_sort()
        selected = []
        update_selection_display()
+       neighbors = {}
+       nneighbors = {}
        for i in faders
                tiles[i].dom.unbind('click').fadeOut fade_ms
+               tiles[i].new_hp = tiles[i].hp
+               for n in board_neighbors[i]
+                       neighbors[n] = tiles[n]
+                       for nn in board_neighbors[n]
+                               nneighbors[nn] = tiles[nn]
+       for i in faders
+               delete nneighbors[i]
+               delete neighbors[i]
+       for k, v of neighbors
+               delete nneighbors[k]
+       # convert to arrays so we can sort, etc
+       nneighbors = (v for k, v of nneighbors)
+       neighbors = (v for k, v of neighbors)
+       boom = [
+               {
+                       tiles: neighbors,
+                       up: [],
+                       down: []
+               },
+               {
+                       tiles: nneighbors,
+                       up: [],
+                       down: []
+               }
+       ]
+       for n in boom
+               for t in n.tiles
+                       if t.hp is 0
+                               n.down.push t
+                       else
+                               n.up.push t
+       switch word_length
+               when 3
+                       boom[0].flips = 1
+                       boom[0].force = 2
+                       boom[1].flips = 0
+                       boom[1].force = 0
+               when 4
+                       boom[0].flips = 'all'
+                       boom[0].force = 4
+                       boom[1].flips = 0
+                       boom[1].force = 2
+               when 5
+                       boom[0].flips = 'all'
+                       boom[0].force = 6
+                       boom[1].flips = 2
+                       boom[1].force = 4
+               when 6
+                       boom[0].flips = 'all'
+                       boom[0].force = 10
+                       boom[1].flips = 5
+                       boom[1].force = 6
+               when 7
+                       boom[0].flips = 'all'
+                       boom[0].force = 10
+                       boom[1].flips = 'all'
+                       boom[1].force = 10
+               else
+                       boom[0].flips = 0
+                       boom[0].force = 0
+                       boom[1].flips = 0
+                       boom[1].force = 0
+                       # unsink/heal the whole board
+                       for t in tiles
+                               if t.hp is 0
+                                       unsink t
+                               else
+                                       t.new_hp = 10
+       for b in boom
+               if b.flips is 'all' or b.flips >= b.down.length
+                       for t in b.down
+                               unsink t
+               else
+                       down_count = b.down.length
+                       while b.flips > 0 and down_count
+                               b.flips -= 1
+                               flipper = Math.floor(Math.random() * down_count)
+                               unsink b.down[flipper]
+                               down_count -= 1
+                               # move the last tile back into range
+                               b.down[flipper] = b.down[down_count]
+               if b.force > 0
+                       for t in b.up
+                               t.new_hp = t.hp + b.force
        for i in tiles
-               unless i in faders
-                       unless i.hp < 1
-                               i.dom.removeClass "hp#{i.hp}"
-                               i.hp -= 1
-                               i.dom.addClass "hp#{i.hp}"
+               i.new_hp ?= i.hp - 1
+               if i.new_hp < 0
+                       i.new_hp = 0
+               else if i.new_hp > HP_MAX
+                       i.new_hp = HP_MAX
+               if i.new_hp isnt i.hp
+                       i.dom.removeClass "hp#{i.hp}"
+                       i.dom.addClass "hp#{i.new_hp}"
+                       i.hp = i.new_hp
+               delete i.new_hp
        timeout fade_ms + 1, ->
                # which tiles need to be slid down
                sliders = (false for i in tiles)
@@ -405,38 +554,33 @@ select_tile = (num) ->
        return
 
 new_tile = (num, x, y) ->
-       letter = new_letter()
+       l = new_letter()
+       letter = l.letter
+       hp = l.hp
 
-       html_tile = $("<div class=\"tile hp10\" style=\"left: #{x}px; top: #{y}px\" unselectable=\"on\">#{letter}</div>")
+       html_tile = $("<div class=\"tile hp#{hp}\" style=\"left: #{x}px; top: #{y}px\" unselectable=\"on\">#{letter}</div>")
        $board.append(html_tile)
 
        html_tile.data 'tile_number', num
-       tiles[num] = text: letter, dom: html_tile, hp: 10, id: num
+       tiles[num] = text: letter, dom: html_tile, hp: hp, id: num
 
        html_tile.click ->
                me = $(this)
                num = me.data 'tile_number'
+               return if tiles[num].hp < 1
                if num in selected
-                       nth_of_word = selected.indexOf(num)
-                       first = nth_of_word is 0
-                       last = nth_of_word is selected.length - 1
-
-                       if first and last
-                               unselect_all() # Clicking only selected letter unselects it
-                       else if first and !last
-                               shrink_selection 1 # Clicking start of word goes back to just that letter
-                               # should this unselect all?
-                       else if last
+                       if selected_word().length > 2 and num is selected.last()
                                activate_selection()
                        else
-                               shrink_selection nth_of_word + 1
-               else # (not clicking on selected tile)
-                       if selected.length is 0
-                               select_tile num
-                       else
-                               unless num in board_neighbors[selected.last()]
+                               if selected.length > 1
                                        unselect_all()
-                               select_tile num
+                                       select_tile num
+                               else
+                                       unselect_all()
+               else # (not clicking on selected tile)
+                       if selected.length > 0 and not (num in board_neighbors[selected.last()])
+                               unselect_all()
+                       select_tile num
 
 
 $board = null
@@ -620,14 +764,10 @@ init_keybinding = ->
                        when 27
                                unselect_all()
 
-$log = undefined
-init_log = ->
-       $log = $('#log')
 log = (msg) ->
-       console.log msg if console.log?
+       console.log msg if console?.log?
 
 init_game = ->
-       init_log()
        if $(window).height() >= 440
                $('#centerer').css('margin-top', '25px')
        init_keybinding()