# typed: ignore

require "stringio"

require_relative "computer"

class Game
  def initialize
    @output = Hash.new(0)
    @min_x, @max_x, @min_y, @max_y = 0, 0, 0, 0
  end

  def []=(x, y, value)
    @output[[x, y]] = value

    @min_x = [@min_x, x].min
    @max_x = [@max_x, x].max
    @min_y = [@min_y, y].min
    @max_y = [@max_y, y].max
  end

  def ball_pos
    @output.key(4)
  end

  def paddle_pos
    @output.key(3)
  end

  def score
    @output[[-1, 0]]
  end

  def to_s
    min_x = [0, @min_x].max

    screen = (@min_y..@max_y).map {|y|
      (min_x..@max_x).map {|x|
        tile(@output[[x, y]])
      }.join
    }.join("\n")

    score = @output[[-1, 0]] || 0

    "#{screen}\n#{score}"
  end

  private

  def tile(id)
    case id
    when 0 then " "
    when 1 then ?█
    when 2 then ?▒
    when 3 then ?▄
    when 4 then ?@
    else fail "unexpected tile id: #{id}"
    end
  end
end

class FollowBall
  def initialize(game)
    @game = game
    @last_ball = nil
  end

  def gets
    ball, paddle = nil, nil
    until [ball, paddle].none?(&:nil?) && ball != @last_ball
      ball = @game.ball_pos
      paddle = @game.paddle_pos
    end
    @last_ball = ball

    # puts @game

    # STDIN.gets
    ball[0] <=> paddle[0]
  end
end

game = Game.new
follow_ball = FollowBall.new(game)
output, output_writer = IO.pipe

Thread.new do
  loop do
    x, y, tile_id = 3.times.map { output.gets.to_i }
    game[x, y] = tile_id
  end
end

Thread.new do
  program = File.read("day_13.txt").split(?,).map(&:to_i)
  program[0] = 2

  Computer.new(program).run(follow_ball, output_writer)

  sleep 1

  puts game.score
end.join