view server.rb @ 0:1eef88068f9f tip

initial commit of maze game source
author ferencd
date Sun, 15 Sep 2019 11:46:47 +0200
parents
children
line wrap: on
line source
require 'net/http/server'
require 'pp'
require 'ellers'
require 'wilsons'
require 'distance_grid'
require 'pg'
require 'base62-rb'
require 'zlib'
require 'wlang'
require 'set'
require 'descends'
require 'pg'
require 'database'
require 'openssl'
require 'game'
require 'gs_logger'
require 'gamedata'

#
# Starting the App, updating things that need to be
#
$LOG.info 'gameserver 0.1 starting'
Database.initialize

#
# Constants
#
CONTENT_TYPE = 'Content-Type'
MAIN_HTML = 'lab.html'
CONTENT_TYPES =
    {
        :png => 'image/png',
        :gif => 'image/gif',
        :js  => 'application/javascript'
    }

GAME_TYPE_MAZE = 1
GAME_TYPE_ADVENTURE = 2
GAME_TYPE_STORY = 3

#################################################################################################################
# Helper classes
#################################################################################################################

#
# Used to get some extra numbers
#
class Integer
  N_BYTES = [42].pack('i').size
  N_BITS = N_BYTES * 16
  MAX = 2 ** (N_BITS - 2) - 1
  MIN = -MAX - 1
end

#
# An extension to the plain string class to provide b62 validations
#
class String
  def valid_b62?
    !!match(/^[[:alnum:]]+$/)
  end

  def is_i?
    /\A[-+]?\d+\z/ === self
  end

  def encrypt(key)
    cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').encrypt
    cipher.key = Digest::SHA1.hexdigest key
    s = cipher.update(self) + cipher.final
    s.unpack('H*')[0].upcase
  end

  def decrypt(key)
    cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').decrypt
    cipher.key = Digest::SHA1.hexdigest key
    s = [self].pack('H*').unpack('C*').pack('c*')
    cipher.update(s) + cipher.final
  end

end

#
# Simple class to NOT to escape HTML code in the wlang renderer
#
class SimpleHtmlRenderer < WLang::Dialect

  def highlight(buf, fn)
    var_name  = render(fn)
    var_value = evaluate(var_name)
    buf << var_value
  end

  tag '$', :highlight
end


#################################################################################################################
#                                           Helper methods                                                      #
#################################################################################################################

#
# Provides a fully completed template for the template engine
#
def complete_templates(templates)
  (1..5).each do | v |
    templates["gotext#{v}".to_sym] = '' unless templates.has_key? "gotext#{v}".to_sym
  end
  templates
end

#
# Just returns page not found
#
def page_not_found
  content_type = {CONTENT_TYPE => 'text/html'}
  retv = [ '<html><center><strong>404 :-(</strong></center></html>' ]
  code = 404
  return code, content_type, retv
end

def internal_server_error
  content_type = {CONTENT_TYPE => 'text/html'}
  retv = [ '<html><center><strong>500 :-(</strong></center></html>' ]
  code = 500
  return code, content_type, retv
end


#
# Will create a random room in the grid
#
def create_room(grid)

  tries = 0
  can_not_make = false
  begin

    max_width = [grid.columns/10, 6].max
    max_height = [grid.rows/10, 6].max

    grid_width = rand(2 .. max_width) + 2
    grid_height = rand(2 .. max_height) + 2

    x1 = rand(grid.columns - grid_width - 4)
    x2 = x1 + grid_width
    y1 = rand(grid.rows - grid_height - 4)
    y2 = y1 + grid_height

    tries += 1
    can_not_make = !grid.can_make_room(x1,y1,x2,y2)

  end while can_not_make && tries > 5

  return '' if tries >= 5

  grid.create_room x1, y1, x2, y2

  # Place the door
  door_x_cell  = x1 + grid_width / 2
  door_y_top_cell = y1
  door_y_bottom_cell = y1 + 1

  "{r:#{door_y_bottom_cell},c:#{door_x_cell},v:0}, {r:#{door_y_top_cell},c:#{door_x_cell},v:1}"
end

#
# Calculates the distance from the goal cell on the given grid towards the cell_to_go
# and returns the final_len and final_path
#
def distance_calculator(goal, grid, cell_to_go, final_len, final_path)
  another_distances = goal.distances
  grid.distances = another_distances.path_to(cell_to_go)
  jsv2 = grid.distances_js_array_path
  final_len += jsv2.length
  final_path += jsv2
  return final_len, final_path
end

#
# Generates a path for the given walker
#
def create_walker_path(grid, r, c)
  # making a new path from the furthest point from there
  new_distances = grid[r, c].distances

  # goal will be the cell which is the furthest from the new_start and max_dist the length of it
  goal, _ = new_distances.max
  grid.distances = new_distances.path_to(goal)

  jsv = grid.distances_js_array_path
  final_path = jsv
  final_len = final_path.length

  # And the goal is that the walker will take a round around the maze

  # Now add in another path to (0,0)
  final_len, final_path = distance_calculator(goal, grid, grid[0, 0],final_len, final_path)
  final_len, final_path = distance_calculator(grid[0, 0], grid, grid[0, grid.columns - 1],final_len, final_path)
  final_len, final_path = distance_calculator(grid[0, grid.columns - 1], grid, grid[grid.rows - 1, grid.columns - 1],final_len, final_path)

  s = ''
  s << "path_len:#{final_len},r:#{r},c:#{c},p:'#{final_path}'"
  s

end

# will generate a few walking characters
def generate_walkers(grid, max_nr)
  walkers = []
  return walkers if max_nr == 0

  walker_count = rand(1 .. max_nr + 1)
  $LOG.info"Generating #{walker_count} walkers\n"
  #walkers << {:body => 0, :row => 0, :col => 1, :path => "path_len:3,r:0,c:1,p:'EEE'"}
  #walkers << {:body => 0, :row => 0, :col => 4, :path => "path_len:3,r:0,c:4,p:'WWW'"}
  dbg_cwa = 0
  (1..walker_count).each do
      walker_body = dbg_cwa #rand(3)
      dbg_cwa = 1 if dbg_cwa == 0
      walker_row = rand(1 .. grid.rows - 2) + 1
      walker_col = rand(1 .. grid.columns - 2) + 1
      walkers <<
                {
                  :body => walker_body,
                  :row => walker_row,
                  :col => walker_col,
                  :path => create_walker_path(grid,walker_row,walker_col)
                }
  end
  walkers
end


#
# Will put the loot stuff on the level. food_locations is an array where wealready have placed food
#
def place_loot(grid, food_locations)
  loot_js = 'var loot=['
  max_possible_loot = grid.dead_ends[1].length + grid.dead_ends[2].length + grid.dead_ends[4].length + grid.dead_ends[8].length
  max_possible_loot = max_possible_loot / 4 + 1

  max_loot_count = $ITEMS.length       # 32 items defined in the png_to_js

  loot_count = [rand(max_possible_loot), max_loot_count].min # We want a lot of loot in the maze

  current_loot_count = 0
  idxs = [1, 2, 4, 8]
  idxsc = 0
  current_idx = idxs[idxsc]
  loot_js_arr = []
  placed_loots = []
  current_loot_value = 0

  (1..loot_count).each do |_|

    place_ctr = 0
    can_place = true
    begin
      cell = grid.dead_ends[current_idx][rand (grid.dead_ends[current_idx].length)]
      place_ctr += 1
      already_placed_here = placed_loots.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column}
      here_is_not_fine = already_placed_here.length > 0

      unless here_is_not_fine
        food_placed_here = food_locations.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column}
        here_is_not_fine |= food_placed_here.length > 0
      end

    end while here_is_not_fine && place_ctr < grid.dead_ends[current_idx].length-1

    if place_ctr == grid.dead_ends[current_idx].length-1
      if already_placed_here.length > 0
        # try to find the first non empty place in the grid
        found = false
        (0..grid.dead_ends[current_idx].length - 1).each do |i|
          cell = grid.dead_ends[current_idx][i]

          already_placed_here = placed_loots.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column}
          can_go_here = already_placed_here.length == 0
          food_placed_here = food_locations.select {|loot_loc| loot_loc[:r] == cell.row && loot_loc[:c] == cell.column}
          can_go_here &= food_placed_here.length == 0

          if can_go_here
            found = true
            break
          end

        end
        can_place = false if found
      end
    end

    if can_place
      loot_js_arr << "{r:#{cell.row},c:#{cell.column},t:#{current_loot_count}}" if (cell.row > 0 && cell.column > 0)

      current_loot_value += $ITEMS[current_loot_count][:value]
      placed_loots << {:r => cell.row, :c => cell.column}
    else
      break
    end

    current_idx = idxs[idxsc]

    idxsc += 1
    if idxsc == idxs.length
      idxsc = 0
    end
    current_loot_count += 1
    if current_loot_count == max_loot_count
      current_loot_count = 0
    end
  end
  loot_js << loot_js_arr.join(',') << "];\n"
  return loot_js, placed_loots, current_loot_value
end

#
# Will put the food on the table ... ie. level
#
def place_food(grid, level_number, game_type)
  food_js = 'var food=['
  max_possible_food = grid.dead_ends[1].length + grid.dead_ends[2].length + grid.dead_ends[4].length + grid.dead_ends[8].length
  max_possible_food = max_possible_food / 4 + 1
  food_count = rand(max_possible_food)

  if game_type == GAME_TYPE_STORY
    if level_number > 55
      food_count *= 4
    elsif level_number > 45
      food_count *= 3
    elsif level_number > 20
      food_count *= 2
    elsif level_number < 10
      food_count /= 2
      food_count += 1
    end
  end

  max_food_count = 10       # This comes from png_to_js the number of possible food items available
  current_food_count = 0    # Counts the food items of the max available
  idxs = [1, 2, 4, 8]       # The indexes of the cells, which are "dead ends".
  idxsc = 0                 # Counter for the "dead end" index array
  current_idx = idxs[idxsc] # The current "dead end" index
  food_js_arr = []          # Will hold the javascript array for the food stuff
  placed_foods = []         # Will hold where the food was placed

  (1..food_count).each do |_|

    place_ctr = 0
    can_place = true

    # Find a place (cell) where we can place the food item
    begin
      cell = grid.dead_ends[current_idx][rand (grid.dead_ends[current_idx].length)]
      place_ctr += 1
      already_placed_here = placed_foods.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column}
    end while already_placed_here.length>0 && place_ctr < grid.dead_ends[current_idx].length-1

    if place_ctr == grid.dead_ends[current_idx].length-1
      # Did not find an "empty dead end" cell to put the food there
      if already_placed_here.length > 0
        # try to find the first empty cell in the grid
        found = false
        (0..grid.dead_ends[current_idx].length - 1).each do |i|
          cell = grid.dead_ends[current_idx][i]
          already_placed_here = placed_foods.select {|food_loc| food_loc[:r] == cell.row && food_loc[:c] == cell.column}
          if already_placed_here.length == 0
            found = true
            break
          end
        end
        can_place = false if found
      end
    end

    if can_place
      food_js_arr << "{r:#{cell.row},c:#{cell.column},t:#{current_food_count}}" if (cell.row > 0 && cell.column > 0)
      placed_foods << {:r => cell.row, :c => cell.column}
    else
      break
    end

    current_idx = idxs[idxsc]

    idxsc += 1
    if idxsc == idxs.length
      idxsc = 0
    end
    current_food_count += 1
    if current_food_count == max_food_count
      current_food_count = 0
    end
  end
  food_js << food_js_arr.join(',') << "];\n"
  return food_js, placed_foods
end

def place_torches(grid)
  torches = grid.identify_torch_locations
  torch_data = []
  torches.each do |torch|
    torch_data << "{i:#{torch[:i]},j:#{torch[:j]},p:#{torch[:number]}}"
  end
  torch_str = "var torch_placements=[#{torch_data.join(',')}];\n"
  storch_str = Utils.split(torch_str, 80)
  storch_str
end

def place_doors(grid, exit_row, exit_col)
  doors_js = []
  # Create a few rooms in the grid
  room_count = 0 #rand(4 .. 10)
  (1..room_count).each do
    room_js = create_room(grid)
    if room_js
      doors_js << room_js
    else
      break
    end
  end
  # 0 -> cell with a top door, 1 -> cell with a bottom door in
  # adding a door at (0,0) just for visual effects :)
  # add another door at (width, height) to indicate exit
  doors_js << '{r:0,c:0,v:0}'
  doors_js << "{r:#{exit_row},c:#{exit_col},v:5}"
  doors_str = "var doors=[ #{doors_js.join(',')}];\nvar exit_row=#{exit_row};\nvar exit_col=#{exit_col};";
  sdoors_str = Utils.split(doors_str, 80)
  sdoors_str
end

def place_weapons(food_locations, grid, height, width)
  weapons_js = 'var weapon_locations = ['
  weapon_locs = []
  all_weapons = [0, 1, 2, 3] # 0 - spear, armor, shoe, ring
  special_weapons = [2, 3].to_set
  shoe_distance = 0
  ring_distance = 0

  all_weapons.each do |weapon|
    distance_len = 101
    begin
      wcol = 1 + rand(width - 2)
      wrow = 1+ rand(height - 2)

      fl = 0
      fp = ''
      wep_cell = grid[wrow, wcol]
      cell00 = grid[0, 0]
      distance_len, _ = distance_calculator(wep_cell, grid, cell00, fl, fp)

      if weapon == 2
        shoe_distance = distance_len
      end

      if weapon == 3
        ring_distance = distance_len
      end

      foods_already_placed_here = food_locations.select {|food_loc| food_loc[:r] == wrow && food_loc[:c] == wcol}
      food_here = foods_already_placed_here.length > 0

      weapons_placed_here = weapon_locs.select {|weapon_loc| weapon_loc[:r] == wrow && weapon_loc[:c] == wcol}
      weapon_here = weapons_placed_here.length > 0

    end while (distance_len > 101 && special_weapons.include?(weapon)) || food_here || weapon_here

    weapon_locs << {:r => wrow, :c => wcol, :t => weapon}
  end
  weap_tmp_arr=[]
  weapon_locs.each {|wpl| weap_tmp_arr << "{r:#{wpl[:r]},c:#{wpl[:c]},t:#{wpl[:t]}}"}
  weapons_js << weap_tmp_arr.join(',') << "];\n"
  return weapons_js, shoe_distance, ring_distance
end

#
# Will create a new maze with the given number as the seed for the random generator.
#
def handle_maze_with_number(level_number, game_id, main_html='lab.html', game_type = GAME_TYPE_STORY)

  current_seed = 0x2ECD06801D ^ level_number
  # Set the seed based on the level number
  srand(current_seed)

  minwidth = 10
  minheight = 10

  maxwidth = 30
  maxheight = 30

  # There are no walkers, so the system should be able to handle it
  if game_type == GAME_TYPE_MAZE
    maxwidth = 40
    maxheight = 40
  end

  maxwidth = [minwidth + level_number, maxwidth].min
  maxheight = [minheight + level_number, maxheight].min

  width =  rand(minwidth .. maxwidth)
  height = rand(minheight ..maxheight)

  $LOG.info "GameID:#{game_id} Generated: #{width} x #{height} for level  #{level_number}"

  grid = DistanceGrid.new(height, width)
  wilson = Wilsons.new
  wilson.on grid

  maze_js = ''
  maze_js << "history.pushState(null, null, '/#{game_id}');"
  maze_js << "var w = #{width};\n"
  maze_js << "var h = #{height};\n"
  maze_js << "#{grid.maze_js_array}\n"

  # Now identify all the places where a torch can go
  storch_str = place_torches(grid)
  maze_js << storch_str

  # Where the exit is for now
  exit_col = width - 1
  exit_row = height- 1


  # At a later stage the exit will not be exactly at the end of the maze, but at the furthest cell from (0,0)
  # start = grid[0,0]
  # distances = start.distances
  # new_start, _ = distances.max
  #if game_type == GAME_TYPE_ADVENTURE
  #  exit_col = new_start.column
  #  exit_row = new_start.row
  #end

  # Calculate the shortest path from the entrance to the exit
  fp_fl = 0
  fp_fp = ''
  fp_wep_cell = grid[exit_row, exit_col]
  fp_cell00 = grid[0,0]
  full_path_len, _ = distance_calculator(fp_wep_cell, grid, fp_cell00, fp_fl, fp_fp)
  $LOG.info "Distance from (0,0) to (#{exit_col}, #{exit_row}) is #{full_path_len} steps"

  loot_value = 0
  walkers = []
  food_locations = []

  # Create a few rooms with doors. Notes:
  # 1. This is not done due to various reasons yet

  sdoors_str = place_doors(grid, exit_row, exit_col)
  maze_js << sdoors_str

  # Create a few walkers but only if the game type is not a simple maze runner
  if game_type != GAME_TYPE_MAZE
    max_walkers = 0
    if game_type == GAME_TYPE_STORY
      max_walkers = [ [1, level_number - 3].max, 15].min if level_number > 4
    else
      max_walkers = rand(3..15)
    end

    walkers = generate_walkers(grid, max_walkers)
    walkers_str_array = []
    walkers.each do |walker|
      walkers_str_array << "{t:#{walker[:body]},#{walker[:path]}}"
    end
    walkers_js = "var walkers=[#{walkers_str_array.join(',')}];\n"
    maze_js << Utils.split(walkers_js, 80)
    maze_js << "var walkers_count = #{walkers.length};"
  else
    maze_js << "var walkers=[];\nvar walkers_count=0;"
  end

  # Food items
  if game_type != GAME_TYPE_MAZE
    food_js, food_locations = place_food(grid, level_number, game_type)
    maze_js << "#{food_js}"
  else
    maze_js << "var food=[];\n"
  end

  # Loot in the maze only if this is an adventure game
  if game_type == GAME_TYPE_ADVENTURE
    loot_js, loot_locations, loot_value = place_loot(grid, food_locations)

    # Just hack the food locations to contain all the loot
    (food_locations << loot_locations).flatten!
  else
    loot_js = "var loot=[];\n"
  end

  maze_js << "#{loot_js}"

  shoe_distance = 0
  ring_distance = 0

  # Weapons
  if game_type != GAME_TYPE_MAZE
    weapons_js,shoe_distance,ring_distance = place_weapons(food_locations, grid, height, width)
    maze_js << "#{weapons_js}"
  else
    maze_js << "var weapon_locations=[];\n"
  end

  templated_maze = IO.binread(main_html)
  if $arrives.has_key? level_number
    templates_arrives = $arrives[level_number]
  else
    templates_arrives = $generic_arrives
  end
  complete_templates templates_arrives

  templates_arrives[:gid] = "#{game_id}"
  templates_arrives[:lid] = "#{level_number}"
  templates_arrives[:page_loader] = 'upon_page_load();'
  # templates_arrives[:page_loader] = 'setup_labyrinth();' if game_type == GAME_TYPE_ADVENTURE or game_type == GAME_TYPE_MAZE

  templates_arrives[:sysmenu_visible] = 'hidden;'
  templates_arrives[:sysmenu_display] = 'none;'
  templates_arrives[:menu_line_visible] = 'visible;'

  level_statistics = "Level Number:#{level_number}\nExit at (#{exit_col}, #{exit_row}).\nDistance from (0,0) to (#{exit_col}, #{exit_row}) is #{full_path_len}"

  templates_arrives[:badbrowser_visible] = 'hidden;'
  templates_arrives[:badbrowser_display] = 'none;'
  templates_arrives[:health_visible] = 'visible'
  templates_arrives[:statis_visible] = 'hidden'
  templates_arrives[:gameid_size] = '105px'
  templates_arrives[:gameid_visible] = 'visible'
  templates_arrives[:equipment_visible] = 'hidden'
  templates_arrives[:messages_visible] = 'hidden; display:none'

  if game_type == GAME_TYPE_ADVENTURE
    templates_arrives[:gold_visible] = 'visible'
    templates_arrives[:skeletons_visible] = 'visible'
    templates_arrives[:levelctr_visible] = 'hidden; display:none'
    templates_arrives[:timer_visible] = 'visible'
    templates_arrives[:day_or_level] = 'Level'
    templates_arrives[:new_game_type] = 'new_adventure_game'
    level_statistics << "\nGold to be found in this dungeon: #{loot_value}\nSkeletons: #{walkers.length}\nSkeletons' acumen: Droidlike\n"
    level_statistics << "Boots of Lightness at #{shoe_distance} steps apart from the start\nRing of Health #{ring_distance} steps apart from the start"

  else
    templates_arrives[:gold_visible] = 'hidden; display:none'
    templates_arrives[:skeletons_visible] = 'hidden; display:none'
    templates_arrives[:levelctr_visible] = 'visible'
    templates_arrives[:timer_visible] = 'hidden; display:none'
    templates_arrives[:new_game_type] = 'new_story_game'
    templates_arrives[:day_or_level] = 'Day'

    if game_type == GAME_TYPE_MAZE
      templates_arrives[:levelctr_visible] = 'hidden; display:none'
      templates_arrives[:health_visible] = 'hidden; display:none'
      templates_arrives[:equipment_visible] = 'hidden; display:none'
      templates_arrives[:timer_visible] = 'visible'
      templates_arrives[:statis_visible] = 'visible'
      templates_arrives[:new_game_type] = 'new_timechaser_game'
    else
      templates_arrives[:messages_visible] = 'visible'
    end

  end

  templates_arrives[:story_game_loc] = Game.gen_game_url 'SG'
  templates_arrives[:adv_game_loc] = Game.gen_game_url 'AG'
  templates_arrives[:timer_game_loc] = Game.gen_game_url 'TG'

  templates_arrives[:level_statistics] = level_statistics

  full_maze = SimpleHtmlRenderer.render(templated_maze, templates_arrives)
  full_maze << '<script type="text/javascript">'


  gt = case game_type
                                     when GAME_TYPE_ADVENTURE then 'GAME_TYPE_ADVENTURE'
                                     when GAME_TYPE_STORY then 'GAME_TYPE_STORY'
                                     when GAME_TYPE_MAZE then 'GAME_TYPE_TIMERUN'
                                     else 'GAME_TYPE_TIMERUN'
                                   end
  javascripts = ''

  javascripts << "const GAME_TYPE_ADVENTURE = 1;\nconst GAME_TYPE_STORY = 2;\nconst GAME_TYPE_TIMERUN = 3\n;var game_type=#{gt};\n"


  javascripts << maze_js

  if game_type == GAME_TYPE_ADVENTURE
    templates_descends = $adventure_descend_summary
  else
    if game_type == GAME_TYPE_STORY
      if $descends.has_key? level_number
        templates_descends =  $descends[level_number]
      else
        templates_descends = $generic_descends
      end
    else
      templates_descends = $timerun_descends
    end
  end

  javascripts << "var story_text =\"#{templates_descends[:story_text]}\";\n"
  javascripts << "var leave_text =\"#{templates_descends[:leave_text]}\";\n"


  javascripts << '</script>'

#  javascripts << '<script type="text/javascript" src="graphics.js"></script>'
#
#  javascripts << '<script type="text/javascript" src="game.js"></script>'

#  templated_game_js = IO.binread 'game.js'
#  game_js = SimpleHtmlRenderer.render(templated_game_js, templates_descends)

#  javascripts << game_js

  full_maze << javascripts

  full_maze << 'request("graphics.js");</script></body></html>'
  retv = [full_maze]

  return 200, retv
end

#
# Handles the root request. Creates a new maze number
#
def handle_root
  templated_maze = IO.binread(MAIN_HTML)
  templates_arrives = $generic_arrives
  complete_templates templates_arrives

  templates_arrives[:gid] = ''
  templates_arrives[:lid] = ''

  templates_arrives[:page_loader] = 'page_loader();'

  templates_arrives[:sysmenu_visible] = 'visible;'
  templates_arrives[:sysmenu_display] = 'block;'
  templates_arrives[:menu_line_visible] = 'hidden;'

  templates_arrives[:gameid_visible] = 'hidden'
  templates_arrives[:gold_visible] = 'hidden'
  templates_arrives[:skeletons_visible] = 'hidden'
  templates_arrives[:levelctr_visible] = 'hidden'
  templates_arrives[:health_visible] = 'hidden'
  templates_arrives[:timer_visible] = 'hidden'
  templates_arrives[:gameid_size] = '0px; padding:0'
  templates_arrives[:day_or_level] = 'Stage'
  templates_arrives[:level_statistics] = 'Nothing here yet'
  templates_arrives[:statis_visible] = 'hidden'
  templates_arrives[:equipment_visible] = 'hidden'
  templates_arrives[:new_game_type] = 'page_loader'
  templates_arrives[:messages_visible] = 'hidden'

  templates_arrives[:badbrowser_visible] = 'hidden;'
  templates_arrives[:badbrowser_display] = 'none;'

  templates_arrives[:story_game_loc] = Game.gen_game_url 'SG'
  templates_arrives[:adv_game_loc] = Game.gen_game_url 'AG'
  templates_arrives[:timer_game_loc] = Game.gen_game_url 'TG'

  full_maze = SimpleHtmlRenderer.render(templated_maze, templates_arrives)
  full_maze << '</body></html>'

  retv = [full_maze]

  return 200, retv
end

#
# Delivers a file to the browser
#
def deliver_file(name)

  code, content_type, retv = page_not_found

  # Will read a file. Supposed to be on the same level as the script
  filename = name[1..-1]
  if File.exists?(filename)

    extension = filename.partition('.').last

    code = 200
    content_type = { CONTENT_TYPE => CONTENT_TYPES[extension] }
    retv = [ IO.binread(name.partition('/').last) ]

  end

  return code, content_type, retv

end

def handle_game_loader(loc)

  begin
    key = loc[0..5]
    enced = loc[6..-1]
    deced = enced.decrypt key
  rescue
    deced = 'SG'
  end
  game_type = case deced
                when 'SG' then
                  deced
                when 'AG' then
                  deced
                when 'TG' then
                  deced
                else
                  'SG'
              end
  code, retv = Game.create_game game_type
  return code, retv
end

def retrieve_game_id(request)
  gid = ''
  gid = request[:headers]['X-Extra'] if request[:headers].has_key? 'X-Extra'
  gid = gid.split('=')[1] if gid && gid.count('=') > 0
  gid
end

def handle_next_level(gid)

  # See where the player was
  level_nr = Database.get_db_level_for_gid gid

  # Move him to the next level
  level_nr += 1
  Database.update_level_number_for_gid(level_nr, gid)

  # And let's play that level
  code, retv = handle_maze_with_number(level_nr, gid, MAIN_HTML, GAME_TYPE_STORY)

  return code, retv
end

#
# Each connection will get in here
#
Net::HTTP::Server.run(:port => 8180) do |request, _|
  begin
  url = request[:uri]

  code, content_type, retv = page_not_found

  lab_id = url[:path]

  # pp lab_id

  # what we are trying to load
  what = lab_id.to_s

  # Handle favico
  if what == '/favicon.ico'
    code = 200
    content_type = {CONTENT_TYPE => 'image/x-icon'}
    retv = [ IO.binread('favicon.ico') ]
  end

  # Nothing, just load the default, and create a new maze
  if what == '/'
    code, retv = handle_root
  elsif what != '/favicon.ico'
    # some file that needs to be delivered?
    if what.end_with?('.js') or what.end_with?('.png') or what.end_with?('.gif')
      code, content_type, retv = deliver_file(what)
    else
      # The actual location
      loc = what[1..-1]

      # See if this is a gametype loader from the main screen, ie full hex string:
      if loc =~ /^[0-9A-F]+$/i and not loc.is_i?
        code, retv = handle_game_loader(loc)
      else
        # gid will be the Game ID of the current game if story mode, otherwise the adventure mode is selected for the
        # current maze
        gid = retrieve_game_id(request)

        # Crazy locations will lead to random maze games
        if loc.length > 9
          loc = 'T' + Utils.random_string(8)
        end
        if gid == loc
          # The player advanced to the next level from the game, we have a valid gid equaling the location of the window
          code, retv = handle_next_level(gid)
        elsif loc.is_i?
          # We get here by a simple "GET" from the browser URL, someone just wants to play a maze game
          # Now update in the DB the level number for gid
          code, retv = handle_maze_with_number(loc.to_i, gid, MAIN_HTML, GAME_TYPE_MAZE)
        elsif loc.length > 0 and (loc[0] == 'S' or loc[0] == 'A' or loc[0] == 'T')
          level_number = Database.get_db_level_for_gid(loc)
          if level_number == -1
            level_number = Zlib::crc32(loc, 0)
          end
          code, retv = handle_maze_with_number(level_number, loc,
                                               MAIN_HTML,
                                               if loc[0] == 'S' then GAME_TYPE_STORY elsif loc[0] == 'T' then GAME_TYPE_MAZE
                                                                                       else GAME_TYPE_ADVENTURE end
                                               )

        end
      end
    end
  end
  rescue => ex
    $LOG.fatal("A Somewhat critical error: #{ex.to_s}")
    code, content_type, retv = internal_server_error
    raise
  end

  # This will go back to the caller
  reply = [code, content_type, retv]
  reply

end