functions.php

<?php


class Game {

    /**
     * array of beam locations that need to be processed.
     *
     * array<string xycords, array location_info> 
     * @param location_info array[x_position, y_position, beam_direction]
     */
    public array $beams_pending = [];

    /** 
     * 0 = up
     * 1 = right
     * 2 = down
     * 3 = left
     */
    public int $beam_direction = DEAD_BEAM;

    /**
     * array<string xycordswdirection, array location_info> 
     * @param location_info array[x_position, y_position, beam_direction]
     *
     */
    public array $beam_locations = [];

    public array $tiles = [];

    /** 
     * x position of tile being processed.
     * 0 is the leftmost column 
     */
    public int $x = 0;

    /** 
     * y position of tile being processed.
     * 1 is the reightmost column
     */
    public int $y = 0;

    /** run the game & build the beam locations array. */
    public function play(array $tiles, int $x, int $y, int $beam_direction){
        $this->tiles = $tiles;
        $this->x = $x;
        $this->y = $y;
        $this->beam_direction = $beam_direction;

        echo "\n\n\nNEW GAME ($x, $y, $beam_direction)\n\n";

        while ($this->beam_direction != DEAD_BEAM){
            //var_dump($this->x);
            //var_dump($this->y);
            $tile = $this->tiles[$this->y][$this->x] ?? null;
            if ($tile===null)$this->kill_beam();
            else {
                $this->tile($tile);
            }
        }
        echo "\n\n\n";

    }

    /**
     * Pass over a tile with the beam.
     */
    public function tile(string $tile){
        //echo $tile;
        static $right_mirror;
        static $left_mirror;
        static $vertical_split;
        static $horizontal_split;

        if (!isset($right_mirror)){
            $right_mirror = [ // the char '/'
                // if the beam is going UP, then it will be changed to RIGHT
                UP => RIGHT,
                RIGHT => UP, 
                DOWN => LEFT,
                LEFT => DOWN,
            ];
            $left_mirror = [ // the char '\'
                // if the beam is going UP, then it will be changed to LEFT
                UP => LEFT,
                RIGHT => DOWN, 
                DOWN => RIGHT,
                LEFT => UP,
            ];
            $vertical_split = [
                RIGHT => [UP, DOWN],
                LEFT => [UP, DOWN],
                UP => [UP],
                DOWN => [DOWN],
            ];
            $horizontal_split = [
                RIGHT => [RIGHT],
                LEFT => [LEFT],
                UP => [RIGHT, LEFT],
                DOWN => [RIGHT, LEFT],
            ];

        }


        //
        // start processing tile/beam
        //

        if ($this->record_beam() == false){
            return;
        }

        if ($tile == '.' ){
            $this->forward();
        } else if ($tile == '/'){
            $this->beam_direction = $right_mirror[$this->beam_direction];
            $this->forward();
        } else if ($tile == '\\'){
            $this->beam_direction = $left_mirror[$this->beam_direction];
            $this->forward();
        } else if ($tile == '|'){
            $beam_directions = $vertical_split[$this->beam_direction];
            $this->beam_direction = $beam_directions[0];
            if (isset($beam_directions[1])){
                $this->beams_pending[] = [BEAM_DIRECTION=>$beam_directions[1], X_POSITION=>$this->x, Y_POSITION=>$this->y];
            }
            $this->forward();
        } else if ($tile == '-'){
            $beam_directions = $horizontal_split[$this->beam_direction];
            $this->beam_direction = $beam_directions[0];
            
            if (isset($beam_directions[1])){
                $this->beams_pending[] = [BEAM_DIRECTION=>$beam_directions[1], X_POSITION=>$this->x, Y_POSITION=>$this->y];
            }
            $this->forward();
        }
    }

    /**
     * Record beam position and direction
     * @return true to continue processing this tile, false otherwise. 
     */
    public function record_beam(): bool {
        $beam_key = $this->x.','.$this->y.':'.$this->beam_direction;
        if (isset($this->beam_locations[$beam_key])){
            $this->kill_beam();
            return false;
            //$this->beam_locations[$xy] = [];
        } else {
            $this->beam_locations[$beam_key] = [X_POSITION=>$this->x, Y_POSITION=> $this->y, BEAM_DIRECTION => $this->beam_direction];
            return true;
        }
    }

    /**
     * Move the beam forward one tile
     */
    public function forward(){
        // up = 0
        // right 1 
        // down 2 
        // left 3
        //
        // if going right, forward is x++ 
        // if left, forward is x--
        // if up, forward is y--
        // if down, forward is y++

        switch ($this->beam_direction){
            case UP: $this->y--; break;
            case RIGHT: $this->x++; break;
            case DOWN: $this->y++; break;
            case LEFT: $this->x--; break;
        }

        if ($this->x < 0 || $this->y < 0)$this->kill_beam();
    }

    public function kill_beam(){
        $new_beam = array_pop($this->beams_pending);
        if ($new_beam == null){
            $this->beam_direction = -1;
            $this->x = -1;
            $this->y = -1;
        }
        else {
            $this->beam_direction = $new_beam[BEAM_DIRECTION];
            $this->x = $new_beam[X_POSITION];
            $this->y = $new_beam[Y_POSITION];
            $this->forward();
        }
    }


    /**
     * Return the number of energized tiles.
     */
    public function show_energized(){
        $energized = [];
        foreach ($this->beam_locations as $beam_key=>$beam){
            $xy = $beam[X_POSITION].','.$beam[Y_POSITION];
            $energized[$xy] = true;
        }
        return count($energized);

        foreach ($this->tiles as $y => $tile_row){
            foreach ($tile_row as $x => $tile){
                $is_energized = isset($energized[$x.','.$y]);
                if ($is_energized) echo "#";
                else echo ".";
            }
            echo "\n";
        }

        echo "\nEnergized Count: ".count($energized);
        return count($energized);
    }

    public function show_directions(){
        $energized = [];
        foreach ($this->beam_locations as $beam_key=>$beam){
            $xy = $beam[X_POSITION].','.$beam[Y_POSITION];
            if (!isset($energized[$xy])){
                $energized[$xy] = $beam;
            }
        }

        $beam_markers = [
            UP => '^',
            RIGHT => '>',
            LEFT => '<',
            DOWN => 'v',
        ];

        echo "\n\n";
        foreach ($this->tiles as $y => $tile_row){
            foreach ($tile_row as $x => $tile){
                $is_energized = isset($energized[$x.','.$y]);
                if ($is_energized && $tile == '.'){
                    $beam_marker = $beam_markers[$energized[$x.','.$y][BEAM_DIRECTION]];
                    echo $beam_marker;
                } else if ($is_energized){
                    echo $tile;
                } else {
                    echo "`";
                }
            }
            echo "\n";
        }

        echo "\nEnergized Count: ".count($energized);
    }
}