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

Angery Spear 0.3.03 #56

Closed
WilleMahMille opened this issue Jul 30, 2022 · 5 comments
Closed

Angery Spear 0.3.03 #56

WilleMahMille opened this issue Jul 30, 2022 · 5 comments
Labels
challenger This issue submits a new challenger

Comments

@WilleMahMille
Copy link

-- The Spear (very simple, easy to counter)
-- Strategy: 
-- We place a large vertical wall down the middle of the playing field. ¨
-- While doing this, the opponent will walk down one side of the wall. We will walk down the same and block off behind us,
-- making the opponent walk a longer distance to their end goal and lose. If the spear is disrupted, 
-- we do our best to still create a spear, although a slightly destroyed one. 

-- Notes:
-- Walls required down the middle: 4
-- Walls to block off one side: 2
-- context.board[x + y*9 + 1] to get coordinates

-- Specifics:
-- The first 3 turns we place a wall
-- The 4th turn we pick a side at random if the enemy haven't picke one, otherwise we pick the same side as the enemy
-- The 5th turn we block off the final part of the middle, aka finising the spear
-- The following 4 turns we walk the shortest path to victory (Turn 6-9)
-- The following 2 turns we block off the enemy (Turn 10-11)
-- After that, we just walk the shortest path to victory (Turn 12+)

require "math"

-- Functions:


-- Pathing functions
function UpdateNearbyTiles(board, x, y, pathLength)
    local update = false
    -- one for each direction
    if not STD__CHECK_OUT_OF_BOUNDS(x+1, y) then
        if pathLength > board[x + y*9 + 2] or board[x + y*9 + 2] == 0 then
            board[x + y*9 + 2] = pathLength
            update = true
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x-1, y) then
        if pathLength > board[x + y*9] or board[x + y*9] == 0 then
            board[x + y*9] = pathLength
            update = true
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y+1) then
        if pathLength > board[x + y*9 + 10] or board[x + y*9 + 10] == 0 then
            board[x + y*9 + 10] = pathLength
            update = true
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y-1) then
        if pathLength > board[x + y*9 - 8] or board[x + y*9  - 8] == 0 then
            board[x + y*9 - 8] = pathLength
            update = true
        end
    end
    return update
end

function GetSmallestNearby(board, x, y)
    local smallest = -100
    if not STD__CHECK_OUT_OF_BOUNDS(x+1, y) then
        if board[x + y*9 +2] > smallest and board[x + y*9 + 2] < 0 then
            smallest = board[x + y*9 + 2]
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x-1, y) then
        if board[x + y*9] > smallest and board[x + y*9] < 0 then
            smallest = board[x + y*9]
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y+1) then
        if board[x + y*9 + 10] > smallest and board[x + y*9 + 10] < 0 then
            smallest = board[x + y*9 + 10]
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y-1) then
        if board[x + y*9 - 8] > smallest and board[x + y*9 - 8] < 0 then
            smallest = board[x + y*9 - 8]
        end
    end
    return smallest
end

function PathFromTile(board, x, y)
    if board[x + y*9 + 1] >= 0 then
        return false
    end
    local pathLength = board[x + y*9 + 1] - 1
    local update = UpdateNearbyTiles(board, x, y, pathLength)
    -- update jump, Edgecase I don't take into consideration, too specific:
    -- player 2 in a top corner, a wall next to player 2 and player 1 one step down from player 2.
    if board[x + y*9 + 1] == 2 then
        local jumpPathLength = GetSmallestNearby(board, x, y) - 1
        if not update then
            update = UpdateNearbyTiles(board, x, y, jumpPathLength)
        else
            UpdateNearbyTiles(board, x, y, jumpPathLength)
        end
    end
    return update
end

function MapPaths(board)
    -- pathfinder function
    -- not sure if it's best to start from where the player is or from where the player wants to go
    -- I think it's best to start from where the player wants to go.
    -- How it works:
    -- the amount of steps required is -1 * value, so we don't interfere with where everything is
    -- we start by making all 0-values in y=0 to 1
    -- we then iterate through the board along the y-directions as long as we change any paths.
    -- each number adds (subtracts bcs negative values) one to itself and sets each tile next to it to
    -- that value if that value is smaller (close to zero/larger) than the value currently on the tile.

    -- max iterations/calculations should be roughly 4*81*81 ~ 26000. expected amount of iterations is roughly 4*9*2*81 ~6400
    for i = 0,8 do
        if board[i+1] == 0 then
            board[i+1] = -1
        end
    end
    local change = true
    for k = 0,30 do
        change = false
        for i = 0,8 do
            for j = 0,8 do
                local update = PathFromTile(board, i, j)
                if not change and update then
                    change = true
                end
            end
        end
    end
end

function WalkShortestPath(context)
    MapPaths(context.board)
    local board = context.board
    local x = context.player.x
    local y = context.player.y
    local smallest = GetSmallestNearby(board, x, y)
    PrevContext = context
    if not STD__CHECK_OUT_OF_BOUNDS(x+1, y) then
        if board[x + y*9 + 2] == smallest then
            return "1"
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x-1, y) then
        if board[x + y*9] == smallest then
            return "3"
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y+1) then
        if board[x + y*9 + 10] == smallest then
            return "2"
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y-1) then
        if board[x + y*9 - 8] == smallest then
            return "0"
        end
    end
end

-- Spear wall function
function BoolToInt(value)
    return value and 1 or 0
end
  
function CheckOneSpearGap(y, context) -- returns flags for if left or right is free
    return BoolToInt(not STD__OCCUPIED(context, 3, y) and not STD__OCCUPIED(context, 4, y)) + 2*BoolToInt(not STD__OCCUPIED(context, 4, y) and not STD__OCCUPIED(context, 5, y))
end

function PlaceSpearLeft(y)
    return "3," .. tostring(y) .. ",4," .. tostring(y)
end

function PlaceSpearRight(y)
    return "4," .. tostring(y) .. ",5," .. tostring(y)
end

function PlaceSpearRandomDirection(y)
    local rand = math.floor(math.random() + 0.5)
    if rand == 0 then
        return PlaceSpearLeft(y)
    end
    return PlaceSpearRight(y)
end

function PlaceSpearWall(y_start, context)
    if y_start > 8 then
        return WalkShortestPath(context) -- all spear walls placed/a player is in the way. 
    end
    -- Step 1: Check if both wall positions are available. If so, place wall
    if ((not STD__OCCUPIED(context, 4, y_start)) and (not STD__OCCUPIED(context, 4, y_start + 1))) then
        return "4," .. tostring(y_start) .. ",4," .. tostring(y_start + 1)
    end
    -- Step 2: Check if both wall positions are occupied. If so, place the next SpearWall
    if (STD__OCCUPIED(context, 4, y_start) and STD__OCCUPIED(context, 4, y_start + 1)) then
        return PlaceSpearWall(y_start + 2, context)
    end
    -- Step 3: Check if it's possible to fill the 1-gap with a wall
    local dirTable = {
        [1] = PlaceSpearLeft,
        [2] = PlaceSpearRight,
        [3] = PlaceSpearRandomDirection
    }
    local direction = CheckOneSpearGap(y_start, context)
    if direction > 0 then
        return dirTable[direction](y_start)
    end
    direction = CheckOneSpearGap(y_start + 1, context)
    if direction > 0 then
        return dirTable[direction](y_start)
    end
    return PlaceSpearWall(y_start + 2, context)
end

-- Functions for side and walling off one sides
function DetermineEnemySide(context)
    local enemy_x = context.opponent.x
    if enemy_x < 4 then
        EnemySide = -1
    end
    if enemy_x > 4 then
        EnemySide = 1
    end
    if enemy_x == 4 then
        EnemySide = 0
    end
end

function PlaceHorizontalWall(x, context)
    -- If we cannot place another wall
    if x > 7 or x < 1 then
        return WalkShortestPath(context)
    end
    -- Step 1: Check if both wall positions are available. If so, place wall
    if (not STD__OCCUPIED(context, x, 0)) and not STD__OCCUPIED(context, x+1, 0) then 
        return tostring(x) .. ",8," .. tostring(x+EnemySide) .. ",8"
    end
    -- Step 2: Check if both wall positions are occupied. If so, place the next Horizontal wall
    if STD__OCCUPIED(context, x, 0) and STD__OCCUPIED(context, x+1, 0) then
        return PlaceHorizontalWall(x+2*EnemySide, context)
    end
    -- Step 3: Check if there is a wall-spot free, otherwise It's blocked and we can place the next Horizontal wall
    if (not STD__OCCUPIED(context, x, 0)) and not STD__OCCUPIED(context, x, 1) then
        return tostring(x) .. ",8," .. tostring(x) .. ",7"
    end
    if (not STD__OCCUPIED(context, x+1, 0)) and not STD__OCCUPIED(context, x+1, 1) then
        return tostring(x+EnemySide) .. ",8," .. tostring(x+EnemySide) .. ",7"
    end
    return PlaceHorizontalWall(x+2*EnemySide, context)
end

-- Turn functions
function TurnOneTwoThree(context)
    if SpearWalls >= 3 then
        -- all the walls have been placed
        return TurnFour(context)
    end
    SpearWalls = context.opponent.y
    SpearWalls = SpearWalls + 2
    return PlaceSpearWall(SpearWalls - 1, context)
end 

function TurnFour(context)
    DetermineEnemySide(context)
    local x = context.player.x
    local y = context.player.y
    if not EnemySide == 0 then
        if not STD__OCCUPIED(context, x+EnemySide, y) then
            return tostring(2+EnemySide)
        end
    end
    return WalkShortestPath(context)
end

function TurnFive(context)
    return PlaceSpearWall(7, context)
end

function WallOffEnemy(context)
    -- no need to stress with walling off horizontally
    if context.opponent.y > 4 then
        DetermineEnemySide(context)
        SideDetermined = true
    end
    if SideDetermined and HorizontalWalls < 2 then
        HorizontalWalls = HorizontalWalls + 1
        return PlaceHorizontalWall(4-EnemySide + EnemySide * 2 * HorizontalWalls, context)
    end
    return WalkShortestPath(context)
end


-- Variables 

Turn = 0
WentFirst = false
EnemySide = 0
SideDetermined = false
Walls = 10
SpearWalls = 0
HorizontalWalls = 0
PreparationTurns = 8
PrevContext = nil

ActionTable = {
    [1] = TurnOneTwoThree,
    [2] = TurnOneTwoThree,
    [3] = TurnOneTwoThree,
    [4] = TurnFour,
    [5] = TurnFive,
    [6] = WalkShortestPath,
    [7] = WalkShortestPath,
    [8] = WallOffEnemy,
}

-- onTurn
-- doesn't follow my vscode addon's naming conventions >-<
function onTurn(context)
    if Turn < PreparationTurns then
        Turn = Turn + 1
    end
    local func = ActionTable[Turn]
    return func(context)
end

-- onJump
function onJump(context)
    context = PrevContext
    local board = context.board
    local x = context.opponent.x
    local y = context.opponent.y
    local smallest = GetSmallestNearby(board, x, y)
    if not STD__CHECK_OUT_OF_BOUNDS(x+1, y) then
        if board[x + y*9 + 2] == smallest then
            return "1"
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x-1, y) then
        if board[x + y*9] == smallest then
            return "3"
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y+1) then
        if board[x + y*9 + 10] == smallest then
            return "2"
        end
    end
    if not STD__CHECK_OUT_OF_BOUNDS(x, y-1) then
        if board[x + y*9 + 10] == smallest then
            return "0"
        end
    end
end

@WilleMahMille WilleMahMille added the challenger This issue submits a new challenger label Jul 30, 2022
@Hampfh
Copy link
Owner

Hampfh commented Jul 30, 2022

[THIS MESSAGE IS AUTOMATIC]
User: WilleMahMille
Script-id: adbd08b3-8504-4b72-83ae-23b843313692
Thanks for submitting!
Your code is being processed...

@Hampfh
Copy link
Owner

Hampfh commented Jul 30, 2022

@Hampfh
Copy link
Owner

Hampfh commented Feb 21, 2023

[THIS MESSAGE IS AUTOMATIC]
[WIN] Opponent: fe128b4c-b947-4420-990a-7658f8c2dc48 | Match

@Hampfh
Copy link
Owner

Hampfh commented Feb 27, 2023

[THIS MESSAGE IS AUTOMATIC]
[LOSS] Opponent: e25da2fb-8bc8-4b19-b6af-69c5a64c6af9 | Match

@Hampfh
Copy link
Owner

Hampfh commented Jun 3, 2023

[THIS MESSAGE IS AUTOMATIC]
[ERROR] Opponent: 6abd680d-558a-4d10-8a94-72ffccfa949c
Error:
Invalid wall placement at ((0,0),(0,1)), coordinates are either occupied or out of bounds

Match

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