<?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);
}
}