Skip to content
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

Abe's first attempt #69

Closed
AbeFrato opened this issue Jun 3, 2023 · 2 comments
Closed

Abe's first attempt #69

AbeFrato opened this issue Jun 3, 2023 · 2 comments
Labels
challenger This issue submits a new challenger

Comments

@AbeFrato
Copy link

AbeFrato commented Jun 3, 2023

-- First attempt by Abe
-- Finds the shortest path to victory using a* pathfinding and attempts to build a maze-like wall structure

require "math"

-- global variables
local goal_list = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}}
local turn_no = 1
local wall_move_no = 1



-- utility function for finding the index of a given element in table
function index_of(table, value)
  for i, v in ipairs(table) do
    if v == value then
        return i
    end
  end
  return nil
end



-- A* HELPER FUNCTIONS
-- use Manhattan distance to get h for a given node
local function geth(context, x, y)
  local min_h = nil
  local h = 0

  for i=1, #goal_list do

    if STD__GET_TILE(context, goal_list[i][1], goal_list[i][2]) < 3 then

      h = math.abs(x - goal_list[i][1]) + math.abs(y - goal_list[i][2])

      if min_h == nil then
        min_h = h
      end
      if h < min_h then
        min_h = h
      end
    end
  end
  return min_h
end

-- get the node with the lowest f value
local function get_lowest_f_node(node_list)
  local min_f_node = node_list[1]
  local current = nil

  for i=2, #node_list do
    current = node_list[i]
    if current.f < min_f_node.f then
      min_f_node = current
    end
  end
  return min_f_node
end

-- check if a node with given coordinates exists in a table, if soo return that node, else return false
local function in_table(table, x, y)
  local node_list = table

  for i = 1, #node_list do
    if node_list[i].x == x and node_list[i].y == y then
      return node_list[i]
    end
  end
  return false
end

-- trace path backwards from given node
local function trace_path(dest)

  local path = {}
  local start_found = false
  local current = dest

  -- build the path, going back through parents
  while start_found ~= true do
    table.insert(path, 1, current)
    if current.parent == nil then
      start_found = true
    else
      current = current.parent
    end
  end

  -- start coordinates
  local start_x = path[1].x
  local start_y = path[1].y

  -- find the desired direction
  if path[2].y == start_y - 1 then
    -- north
    return "0"
  elseif path[2].x == start_x - 1 then
    -- west
    return "3"
  elseif path[2].x == start_x + 1 then
    -- east
    return "1"
  else
    -- south
    return "2"
  end

end



-- Node object definition
local Node = {}

-- constructor
local function Node_init(x, y, f, parent)
  local self = setmetatable({}, Node)
  self.x = x
  self.y = y
  self.f = f
  self.parent = parent
  return self
end



-- A* pathfinding algorithm
local function a_star(context)
  local move_count = 0
  local open_list = {}
  local closed_list = {}

  -- init starting node
  local start = Node_init(context.player.x, context.player.y, 0, nil)

  -- put starting node in open list
  table.insert(open_list, start)

  -- loop through the open list
  while #open_list > 0 do
        
    -- get the node with lowest f in the open list
    current = get_lowest_f_node(open_list)
    -- remove it from said list
    table.remove(open_list, index_of(open_list, current))
    -- add it to the closed list
    table.insert(closed_list, current)

    -- north
    if STD__CHECK_OUT_OF_BOUNDS(current.x, current.y-1) == false then
      if STD__GET_TILE(context, current.x, current.y-1) < 3 then
        if current.y-1 == 0 then
          -- do destination stuff
          local dest_node = Node_init(current.x, current.y-1, 0, current)
          return trace_path(dest_node)
        end

        -- ignore if in closed list already
        if in_table(closed_list, current.x, current.y-1) == false then
          local north = Node_init(current.x, current.y-1, (move_count + geth(context, current.x, current.y-1)), current)

          -- if not in open list, add it
          if in_table(open_list, north.x, north.y) == false then
            table.insert(open_list, north)

          -- else check if corresponding node in open list has a lower f value. If not, update its values
          else
            local north_in_open_list = in_table(open_list, north.x, north.y)
            if north.f < north_in_open_list.f then
              north_in_open_list.f = north.f
              north_in_open_list.x = north.x
              north_in_open_list.y = north.y
              north_in_open_list.parent = north.parent
            end
          end
        end
      end
    end

    -- west
    if STD__CHECK_OUT_OF_BOUNDS(current.x-1, current.y) == false then
      if STD__GET_TILE(context, current.x-1, current.y) < 3 then

        -- ignore if in closed list already
        if in_table(closed_list, current.x-1, current.y) == false then
          local west = Node_init(current.x-1, current.y, (move_count + geth(context, current.x-1, current.y)), current)

          -- if not in open list, add it
          if in_table(open_list, west.x, west.y) == false then
            table.insert(open_list, west)

          -- else check if corresponding node in open list has a lower f value. If not, update its values
          else
            local west_in_open_list = in_table(open_list, west.x, west.y)
            if west.f < west_in_open_list.f then
              west_in_open_list.f = west.f
              west_in_open_list.x = west.x
              west_in_open_list.y = west.y
              west_in_open_list.parent = west.parent
            end
          end
        end
      end
    end
    
    -- east
    if STD__CHECK_OUT_OF_BOUNDS(current.x+1, current.y) == false then
      if STD__GET_TILE(context, current.x+1, current.y) < 3 then

        -- ignore if in closed list already
        if in_table(closed_list, current.x+1, current.y) == false then
          local east = Node_init(current.x+1, current.y, (move_count + geth(context, current.x+1, current.y)), current)

          -- if not in open list, add it
          if in_table(open_list, east.x, east.y) == false then
            table.insert(open_list, east)

          -- else check if corresponding node in open list has a lower f value. If not, update its values
          else
            local east_in_open_list = in_table(open_list, east.x, east.y)
            if east.f < east_in_open_list.f then
              east_in_open_list.f = east.f
              east_in_open_list.x = east.x
              east_in_open_list.y = east.y
              east_in_open_list.parent = east.parent
            end
          end
        end
      end
    end

    -- south
    if STD__CHECK_OUT_OF_BOUNDS(current.x, current.y+1) == false then
      if STD__GET_TILE(context, current.x, current.y+1) < 3 then

        -- ignore if in closed list already
        if in_table(closed_list, current.x, current.y+1) == false then
          local south = Node_init(current.x, current.y+1, (move_count + geth(context, current.x, current.y+1)), current)

          -- if not in open list, add it
          if in_table(open_list, south.x, south.y) == false then
            table.insert(open_list, south)

          -- else check if corresponding node in open list has a lower f value. If not, update its values
          else
            local south_in_open_list = in_table(open_list, south.x, south.y)
            if south.f < south_in_open_list.f then
              south_in_open_list.f = south.f
              south_in_open_list.x = south.x
              south_in_open_list.y = south.y
              south_in_open_list.parent = south.parent
            end
          end
        end
      end
    end
    move_count = move_count + 1
  end
end



-- WALL BUILDING FUNCTIONS
-- make sure row 7 is clear
local function row_7_clear(context)
  local clear = true
  for i=0, 8 do
    if STD__GET_TILE(context, i, 7) == 3 then
      clear = false
    end
  end
  return clear
end

-- check so that opponents goal line is somewhat clear (should be more elaborate really)
local function goal_line_relatively_clear(context, up_to_x)
  local walls = 0
  for i=0, up_to_x-1 do
    if STD__GET_TILE(context, i, 8) == 3 then
      walls = walls + 1
    end
  end
  if walls == up_to_x then
    return false
  else
    return true
  end
end

-- attempt to build the maze (optimize with functions later perhaps)
local function build_maze(context)

  if wall_move_no == 1 then
    if STD__GET_TILE(context, 7, 8) == 0 and STD__GET_TILE(context, 8, 8) == 0 and goal_line_relatively_clear(context, 7) == true then
      wall_move_no = 2
      return "7,8,8,8"
    else
      return a_star(context)
    end

  elseif wall_move_no == 2 then
    if STD__GET_TILE(context, 5, 8) == 0 and STD__GET_TILE(context, 6, 8) == 0 and goal_line_relatively_clear(context, 5) == true then
      wall_move_no = 3
      return "5,8,6,8"
    else
      return a_star(context)
    end

  elseif wall_move_no == 3 then
    if STD__GET_TILE(context, 3, 8) == 0 and STD__GET_TILE(context, 4, 8) == 0 and goal_line_relatively_clear(context, 3) == true then
      wall_move_no = 4
      return "3,8,4,8"
    else
      return a_star(context)
    end
  
  elseif wall_move_no == 4 then
    if STD__GET_TILE(context, 1, 8) == 0 and STD__GET_TILE(context, 2, 8) == 0 and goal_line_relatively_clear(context, 1) == true then
      wall_move_no = 5
      return "1,8,2,8"
    else
      return a_star(context)
    end
  
  elseif wall_move_no == 5 then
    if row_7_clear(context) == true and STD__GET_TILE(context, 0, 6) == 0 and STD__GET_TILE(context, 1, 6) == 0 then
      wall_move_no = 6
      return "0,6,1,6"
    else
      return a_star(context)
    end

  elseif wall_move_no == 6 then
    if row_7_clear(context) == true and STD__GET_TILE(context, 2, 6) == 0 and STD__GET_TILE(context, 3, 6) == 0 then
      wall_move_no = 7
      return "2,6,3,6"
    else
      return a_star(context)
    end

  elseif wall_move_no == 7 then
    if row_7_clear(context) == true and STD__GET_TILE(context, 4, 6) == 0 and STD__GET_TILE(context, 5, 6) == 0 then
      wall_move_no = 8
      return "4,6,5,6"
    else
      return a_star(context)
    end

  elseif wall_move_no == 8 then
    if row_7_clear(context) == true and STD__GET_TILE(context, 6, 6) == 0 and STD__GET_TILE(context, 7, 6) == 0 then
      wall_move_no = 9
      return "6,6,7,6"
    else
      return a_star(context)
    end
  
  else
    return a_star(context)
  end
end



-- the stuff that gets called
function onTurn(context)
  if turn_no == 1 then
    turn_no = turn_no + 1
    return a_star(context)

  elseif 1 < turn_no and turn_no <= 5 then
    turn_no = turn_no + 1
    return build_maze(context)

  elseif 5 < turn_no and turn_no <= 8 then
    turn_no = turn_no + 1
    return a_star(context)

  elseif 8 < turn_no and turn_no <= 12 then
    turn_no = turn_no + 1
    return build_maze(context)

  else
    turn_no = turn_no + 1
    return a_star(context)
  end
end

function onJump(context)
  local x = context.player.x
  local y = context.player.y
  if STD__GET_TILE(context, x, y-2) == 0 or y-2 == -1 then
    return "0"
  elseif STD__GET_TILE(context, x+1, y-1) == 0 then
    return "1"
  elseif STD__GET_TILE(context, x-1, y-1) == 0 then
    return "2"
  else
    return "3"
  end
end
@AbeFrato AbeFrato added the challenger This issue submits a new challenger label Jun 3, 2023
@Hampfh
Copy link
Owner

Hampfh commented Jun 3, 2023

[THIS MESSAGE IS AUTOMATIC]
User: AbeFrato
Script-id: 6abd680d-558a-4d10-8a94-72ffccfa949c
Thanks for submitting!
Your code is being processed...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
challenger This issue submits a new challenger
Projects
None yet
Development

No branches or pull requests

2 participants