DEV Community

Cover image for Advent of Code 2019 Solution Megathread - Day 11: Space Police
Jon Bristow
Jon Bristow

Posted on

Advent of Code 2019 Solution Megathread - Day 11: Space Police

IntCode's back, but his brain is inside the robot now.

Day 11 - The Problem

Oh no! The police have pulled us over! Apparently, not properly displaying registration markings is a bookable offense. Luckily, we are able to get our registration from the control-room elves. We just need our hull painting robot to take the code and paint our mark. We do have a hull painting robot, don't we?

Part 1 has us building an IntCode interpreter powered robot. We need to send the interpreter input (the color of the hull where it's standing) and use the output to move the robot and paint the hull.

Unfortunately, the simulation goes awry, and the robot paints a deliriously beautiful but meaningless picture. Worse, it's not a registration mark. We re-calibrate our starting color, and successfully. generate our registration code to complete Part 2.

Ongoing Meta

Dev.to List of Leaderboards

If you were part of Ryan Palo's leaderboard last year, you're still a member of that!

If you want me to add your leaderboard code to this page, reply to one of these posts and/or send me a DM containing your code and any theming or notes you’d like me to add. (You can find your private leaderboard code on your "Private Leaderboard" page.)

I'll edit in any leaderboards that people want to post, along with any description for the kinds of people you want to have on it. (My leaderboard is being used as my office's leaderboard.) And if I get something wrong, please call me out or message me and I’ll fix it ASAP.

There's no limit to the number of leaderboards you can join, so there's no problem belonging to a "Beginner" and a language specific one if you want.

Neat Statistics

I'm planning on adding some statistics, but other than "what languages did we see yesterday" does anyone have any ideas?

Languages Seen On Day 10
  • JavaScript x 2
  • Python x 2
  • Kotlin
  • Swift
Completion Stats

By applying a best-fit descending power trend-line to the 1 and 2 star columns on the stats page, my estimates predict:

2 stars on day 25: 6358 people

1 star on day 25: 420 people. Nice

If you're still going, take heart in that you are in a rapidly dwindling pool of people.

Doing other questionable statistics gives me: 415/6248

Top comments (11)

Collapse
 
rizzu26 profile image
Rizwan

bit late but here you go Swift solution

class Opcode {
    var numbers: [Int]
    var inputIds: [Int] = []
    var inputId: Int
    var done = false
    var index = 0
    var relativeBase = 0
    var output: Int!
    var outputs: [Int] = []

    init(_ input: [Int], _ inputId: Int = 0) {
        self.numbers = input
        numbers.append(contentsOf: Array.init(repeating: 0, count:1000))
        self.inputId = inputId
        self.inputIds.append(inputId)
    }

    func run() -> Int {
        guard done == false else { return output }
        while true {
            let digits = numbers[index]
            let opCode = ((digits.digits.dropLast().last ?? 0) * 10) + digits.digits.last!
            var instruction = digits.digits
            //print("Numbers \(numbers)")
            //print("Running opcode \(instruction)")

            func getValue(_ aIndex: Int, _ mode: Int = 0) -> Int {
                switch mode {
                case 0:
                    return numbers[numbers[index + 1 + aIndex]];
                case 1:
                    return numbers[index + 1 + aIndex];
                case 2:
                    return numbers[safe: relativeBase + numbers[index + 1 + aIndex]] ?? 0;
                default:
                    fatalError("unexpected mode \(mode)")
                }
            }

            func setValue(_ index: Int, _ value: Int, _ mode: Int = 0) {
                //print("set index is : \(index) value s \(value) mode is \(mode)")
                switch mode {
                case 0:
                    numbers[index] = value
                //print("set index is : \(index)")
                case 2:
                    //print(relativeBase + index)
                    numbers[relativeBase + index] = value
                default:
                    break
                }
            }

            func getParam(_ aIndex: Int) -> Int {
                if let mode = instruction.dropLast(2 + aIndex).last {
                    //print("mode is : \(mode)")
                    //print("value is : \(getValue(aIndex,mode))")
                    return getValue(aIndex,mode)
                }
                return getValue(aIndex)
            }

            func setParam(_ aIndex: Int, _ value: Int) {
                //print("set value is : \(value)")
                //print("setting apram \(aIndex))")
                if let mode = instruction.dropLast(2 + aIndex).last {
                    setValue(aIndex, value, mode)
                    return
                }
                setValue(aIndex, value)
            }

            func getMode(_ aIndex: Int) -> Int {
                //print(instruction)
                if let mode = instruction.dropLast(2 + aIndex).last {
                    return mode
                }
                return 0
            }

            switch opCode {
            case 1:
                let p1 = getParam(0)
                let p2 = getParam(1)
                let p3 = numbers[index + 3]
                setValue(p3, p1 + p2,getMode(2))
                index += 4

            case 2:
                let p1 = getParam(0)
                let p2 = getParam(1)
                let p3 = numbers[index + 3]
                setValue(p3, p1 * p2,getMode(2))
                index += 4

            case 3:
                let p1 = numbers[index + 1]
                setValue(p1, inputIds.last!,getMode(2))
                //                setParam(p1, inputIds.last!)
                index += 2
            case 4:
                let p1 = getParam(0)
                output = p1
                //print("output is :\(p1)")
                index += 2
                return output

            case 5:
                let p1 = getParam(0)
                let p2 = getParam(1)
                if p1 != 0 {
                    index = p2
                }
                else {
                    index += 3
                }

            case 6:
                let p1 = getParam(0)
                let p2 = getParam(1)
                if p1 == 0 {
                    index = p2
                }
                else {
                    index += 3
                }

            case 7:
                let p1 = getParam(0)
                let p2 = getParam(1)
                let p3 = numbers[index + 3]
                setValue(p3, p1 < p2 ? 1 : 0,getMode(2))
                index += 4

            case 8:
                let p1 = getParam(0)
                let p2 = getParam(1)
                let p3 = numbers[index + 3]
                setValue(p3, p1 == p2 ? 1 : 0,getMode(2))
                index += 4

            case 9:
                let p1 = getParam(0)
                relativeBase += p1
                index += 2
                //            print("New R Base \(relativeBase)")

            case 99:
                done = true
                return output

            default:
                fatalError("Unknown opcode \(opCode)")
            }
        }
    }
}

enum Direction: Int {
    case up = 0
    case left = 1
    case right = 2
    case down = 3

    mutating func turn(_ direction: Int) {
        if direction == 0 {
            switch self {
            case .up:
                self = Direction.left
            case .down:
                self = Direction.right
            case .left:
                self = Direction.down
            case .right:
                self = Direction.up
            }
        }
        else if direction == 1 {
            switch self {
            case .up:
                self = Direction.right
            case .down:
                self = Direction.left
            case .left:
                self = Direction.up
            case .right:
                self = Direction.down
            }
        }

    }
}

struct Point: CustomStringConvertible, Equatable, Hashable {
    var x: Int
    var y: Int

    var description: String {
        get {
            return "X: \(self.x) Y: \(self.y)"
        }
    }

    mutating func move(in direction: Direction) {
        switch direction {
        case .up: self.y += 1
        case .right: self.x += 1
        case .down: self.y -= 1
        case .left: self.x -= 1
        }
    }
}

extension Array where Element : Hashable {
    var unique: [Element] {
        return Array(Set(self))
    }
}
enum Color: Int {
    case black = 0
    case white = 1
}

class Robot {
    let startColor: Color
    var paint: [Point: Int] = [:]

    init(_ startColor: Color) {
        self.startColor = startColor
    }

    @discardableResult func run() -> Int {
        let computer = Opcode(input,startColor.rawValue)
        var direction: Direction = .up
        var currentPosition = Point(x: 0, y: 0)
        paint = [currentPosition:0]
        while !computer.done {
            let color = computer.run()
            let turn = computer.run()

            paint[currentPosition] = color
            direction.turn(turn)
            currentPosition.move(in: direction)
            computer.inputIds.append(paint[currentPosition] ?? 0)
        }
        return Set(paint.keys).count
    }

    func paintHull() {
        let minX = paint.keys.min{ a,b in a.x < b.x }!.x
        let minY = paint.keys.min{ a,b in a.y < b.y }!.y

        let maxX = paint.keys.min{ a,b in a.x > b.x }!.x
        let maxY = paint.keys.min{ a,b in a.y > b.y }!.y

        var line: [[String]] = []
        for y in (minY ..< maxY+1) {
            var row: [String] = []
            for x in (minX ..< maxX+1) {
                let point = Point.init(x: x, y: y)
                let char = paint[point]
                row.append(char == Color.white.rawValue ? "#" : " ")
            }
            line.insert(row, at: 0)
        }
        for aLine in line {
            print(aLine, separator:"")
        }
    }
}



func partOne() {
    let result = Robot(.black).run()
    print("Part 1 answer is :\(result)")
}

func partTwo() {
    let robot = Robot(.white)
    robot.run()
    robot.paintHull()
}

partOne()
partTwo()
Collapse
 
aspittel profile image
Ali Spittel

Python!

def get_modes(modes):
    return [int(mode) for mode in [modes[2], modes[1], modes[0], modes[3:]]]


class Computer:
    def __init__(self, data):
        self.idx = 0
        self.data = data[:] + [0] * 3000
        self.done = False
        self.output = None
        self.inputs = []
        self.relative_base = 0

    def get_params(self, mode1, mode2, mode3):
        return self.get_param(mode1, 1), self.get_param(mode2, 2), self.get_param(mode3, 3)

    def get_param(self, mode, increment):
        if mode == 0:
            return self.data[self.idx + increment]
        elif mode == 1:
            return self.idx + increment
        else:
            return self.relative_base + self.data[self.idx + increment]

    def add(self, mode1, mode2, mode3):
        param1, param2, param3 = self.get_params(mode1, mode2, mode3)
        self.data[param3] = self.data[param1] + self.data[param2]
        self.idx += 4

    def multiply(self, mode1, mode2, mode3):
        param1, param2, param3 = self.get_params(mode1, mode2, mode3)
        self.data[param3] = self.data[param1] * self.data[param2]
        self.idx += 4

    def take_input(self, mode1):
        param1 = self.get_param(mode1, 1)
        self.data[param1] = self.inputs.pop(0)
        self.idx += 2

    def create_output(self, mode1):
        param1 = self.get_param(mode1, 1)
        self.output = self.data[param1]
        self.idx += 2
        return self.output

    def less_than(self, mode1, mode2, mode3):
        param1, param2, param3 = self.get_params(mode1, mode2, mode3)
        self.data[param3] = 1 if self.data[param1] < self.data[param2] else 0
        self.idx += 4

    def equals(self, mode1, mode2, mode3):
        param1, param2, param3 = self.get_params(mode1, mode2, mode3)
        self.data[param3] = 1 if self.data[param1] == self.data[param2] else 0
        self.idx += 4

    def jump_if_true(self, mode1, mode2, mode3):
        param1, param2, param3 = self.get_params(mode1, mode2, mode3)
        self.idx = self.data[param2] if self.data[param1] != 0 else self.idx + 3

    def jump_if_false(self, mode1, mode2, mode3):
        param1, param2, param3 = self.get_params(mode1, mode2, mode3)
        self.idx = self.data[param2] if self.data[param1] == 0 else self.idx + 3

    def relative_offset(self, mode1):
        param1 = self.get_param(mode1, 1)
        self.relative_base += self.data[param1]
        self.idx += 2

    def calculate(self, input_val=None):
        if input_val is not None: self.inputs.append(input_val)
        modes = {
            1: lambda: self.add(mode1, mode2, mode3),
            2: lambda: self.multiply(mode1, mode2, mode3),
            3: lambda: self.take_input(mode1),
            5: lambda: self.jump_if_true(mode1, mode2, mode3),
            6: lambda: self.jump_if_false(mode1, mode2, mode3),
            7: lambda: self.less_than(mode1, mode2, mode3),
            8: lambda: self.equals(mode1, mode2, mode3),
            9: lambda: self.relative_offset(mode1)
        }
        while True:
            mode1, mode2, mode3, opcode = get_modes(f"{self.data[self.idx]:05}")
            if opcode in modes:
                modes[opcode]()              
            elif opcode == 4:
                return self.create_output(mode1)                
            elif opcode == 99:
                self.done = True
                return self.output


class Painting:
    def __init__(self, input_vals, initial_color=0):
        self.computer = Computer(input_vals)
        self.direction = 0
        self.x, self.y = 0, 0
        self.painted = {(self.x, self.y): initial_color}

    def paint(self):
        while not self.computer.done:
            starting_color = self.painted[(self.x, self.y)] if (self.x, self.y) in self.painted else 0
            self.painted[(self.x, self.y)] = self.computer.calculate(starting_color)
            self.change_direction(self.computer.calculate())
            self.rotate()

    def change_direction(self, rotate_direction):
        if rotate_direction == 0:
            self.direction = (self.direction - 1) % 4
        else:
            self.direction = (self.direction + 1) % 4

    def rotate(self):
        if self.direction == 0:
            self.y += 1
        elif self.direction == 1:
            self.x += 1
        elif self.direction == 2:
            self.y -= 1
        elif self.direction == 3:
            self.x -= 1        

    def show_painting(self):
        data = [[" " for _ in range(50)] for _ in range(6)]
        for x, y in self.painted.keys():
            color = self.painted[(x, y)]
            data[abs(y)][x] = " " if color == 0 else "|"
        for row in data:
            print(''.join(row))


with open("input.txt") as _file:
    for line in _file:
        input_vals = [int(num) for num in line.split(",")]
        painting = Painting(input_vals)
        painting.paint()
        print(f"Part 1: {len(painting.painted.keys())}")

        letter_painting = Painting(input_vals, 1)
        letter_painting.paint()
        print(f"Part 2: ")
        letter_painting.show_painting()
Collapse
 
maxart2501 profile image
Massimo Artizzu

I knew we were going to use IntCodes again! 🧐

But fortunately at this point I'm pretty confident of my routines, so I just copy+pasted most of it. Tweaked a bit since after each output it could or could not receive an input (the scanned color). Also ditched BigInts since they are not necessary.

So the main running cycle became (JavaScript ahead!):

for (const instruction of getInstructions()) {
  if (instruction.opcode === 99) {
    return;
  }
  const output = execute(instruction);
  if (typeof output === 'number') {
    const newValue = yield output;
    if (typeof newValue !== 'undefined') {
      stack.push(newValue);
    }
  }
}

For the rest, I'm keeping track of the coordinates, the direction (an integer between 0 and 3) and the last read panel color. Everything worked at the first try!

Part One

// `createProgramInstance` as defined on day 9, with the above corrections
const paintedPanels = new Map();
const robot = createProgramInstance(codes, 0);
let direction = 0;
let row = 0;
let column = 0;
let currentPaint;
while (true) {
  const { value: paint } = robot.next(currentPaint);
  const coords = column + ',' + row;
  if (paintedPanels.get(coords) === paint) {
    break; // It breaks out after 9515 cycles for me...
  }
  paintedPanels.set(coords, paint);
  const { value: turn } = robot.next();
  direction = (direction + (turn === 0 ? 3 : 1)) % 4;
  switch (direction) {
    case 0: row--; break;
    case 1: column++; break;
    case 2: row++; break;
    case 3: column--; break;
  }
  currentPaint = paintedPanels.get(column + ',' + row) || 0;
}
console.log(paintedPanels.size);

Part Two

// As above, except it starts with 1 instead of 0:
const robot = createProgramInstance(codes, 1);

// This comes instead of the last console.log
const paintedPanelsCoords = [ ...paintedPanels.keys() ].map(coords => coords.split(','));
const paintedColumns = paintedPanelsCoords.map(([ column ]) => column);
const paintedRows = paintedPanelsCoords.map(([ , row ]) => row);
const minColumn = Math.min(...paintedColumns);
const maxColumn = Math.max(...paintedColumns);
const minRow = Math.min(...paintedRows);
const maxRow = Math.max(...paintedRows);

// Pretty printing the result
for (let row = minRow; row <= maxRow; row++) {
  const line = Array.from({ length: maxColumn - minColumn + 1 }, (_, index) => {
    return paintedPanels.get(minColumn + index + ',' + row) ? '#' : '.';
  }).join('');
  console.log(line);
}
Collapse
 
nordfjord profile image
Einar Norðfjörð

Solution in JS

I used an object to represent the grid to make counting the "painted" block easier.

Had a bit of trouble realizing that the top left of the grid is 0,0 and not the bottom left :)

const INSTRUCTIONS = {
  ADD: 1,
  MULT: 2,
  INPUT: 3,
  OUTPUT: 4,
  JUMP_IF_TRUE: 5,
  JUMP_IF_FALSE: 6,
  LESS_THAN: 7,
  EQUALS: 8,
  ADJUST_RELATIVE_BASE: 9,
  HALT: 99
}

function runProgram(instructions) {
  instructions = instructions.slice()

  return function* amplifier() {
    let lastOutput = null
    let relativeBase = 0
    for (let i = 0; i < instructions.length; ++i) {
      const instruction = instructions[i]
      const parsed = String(instruction)
        .padStart(5, '0')
        .split('')
      const getValue = (value, mode = '0') => {
        if (mode === '0') {
          return instructions[value] || 0
        } else if (mode === '1') {
          return value
        } else if (mode === '2') {
          return instructions[relativeBase + value] || 0
        }
      }
      const setValue = (index, value, mode = '0') => {
        if (mode === '0') {
          instructions[index] = value
        } else if (mode === '2') {
          instructions[relativeBase + index] = value
        }
      }
      const opCode = Number(parsed.slice(3).join(''))
      const modes = parsed.slice(0, 3)
      switch (opCode) {
        case INSTRUCTIONS.ADD: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x + y, modes[0])
          break
        }
        case INSTRUCTIONS.MULT: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x * y, modes[0])
          break
        }
        case INSTRUCTIONS.INPUT: {
          setValue(instructions[++i], yield { type: 'INPUT' }, modes[2])
          break
        }
        case INSTRUCTIONS.OUTPUT: {
          lastOutput = getValue(instructions[++i], modes[2])
          yield { type: 'OUTPUT', value: lastOutput }
          break
        }
        case INSTRUCTIONS.JUMP_IF_TRUE: {
          const compare = getValue(instructions[++i], modes[2])
          const jumpTo = getValue(instructions[++i], modes[1]) - 1
          if (compare != 0) {
            i = jumpTo
          }
          break
        }
        case INSTRUCTIONS.JUMP_IF_FALSE: {
          const compare = getValue(instructions[++i], modes[2])
          const jumpTo = getValue(instructions[++i], modes[1]) - 1
          if (compare == 0) {
            i = jumpTo
          }
          break
        }
        case INSTRUCTIONS.LESS_THAN: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x < y ? 1 : 0, modes[0])
          break
        }
        case INSTRUCTIONS.EQUALS: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x === y ? 1 : 0, modes[0])
          break
        }
        case INSTRUCTIONS.ADJUST_RELATIVE_BASE: {
          const adjustBy = getValue(instructions[++i], modes[2])
          relativeBase += adjustBy
          break
        }
        case INSTRUCTIONS.HALT:
          return lastOutput
      }
    }
  }
}

const directions = {
  UP: 0,
  RIGHT: 1,
  DOWN: 2,
  LEFT: 3
}
const modulo = (x, n) => ((x % n) + n) % n

const runRobotProgram = (robot, initialBlockColor = 0) => {
  const grid = {}
  let location = [0, 0]
  grid[location.join(':')] = initialBlockColor
  let direction = directions.UP
  robot.next()
  while (true) {
    const input = grid[location.join(':')] || 0
    const {
      value: { value: color }
    } = robot.next(input)
    grid[location.join(':')] = color
    const {
      value: { value: turn }
    } = robot.next()
    const { done } = robot.next()
    if (done) return { grid }
    if (turn === 0) {
      direction = modulo(direction - 1, 4)
    } else if (turn === 1) {
      direction = (direction + 1) % 4
    }

    if (direction === directions.DOWN) {
      location = [location[0], location[1] + 1]
    } else if (direction === directions.RIGHT) {
      location = [location[0] + 1, location[1]]
    } else if (direction === directions.UP) {
      location = [location[0], location[1] - 1]
    } else if (direction === directions.LEFT) {
      location = [location[0] - 1, location[1]]
    }
  }
}

function printGrid(grid) {
  const [[minX, maxX], [minY, maxY]] = Object.keys(grid)
    .map(x => x.split(':').map(Number))
    .reduce(
      (state, [x, y]) => {
        if (state[0][0] > x) state[0][0] = x
        if (state[0][1] < x) state[0][1] = x
        if (state[1][0] > y) state[1][0] = y
        if (state[1][1] < y) state[1][1] = y
        return state
      },
      [
        [Infinity, -Infinity],
        [Infinity, -Infinity]
      ]
    )

  let gridMap = ''
  for (let y = minY; y <= maxY; ++y) {
    for (let x = minX; x <= maxX; ++x) {
      gridMap += grid[`${x}:${y}`] === 1 ? '#' : ' '
    }
    gridMap += '\n'
  }
  return gridMap
}

function part1(instructions) {
  const { grid } = runRobotProgram(runProgram(instructions)())

  const gridMap = printGrid(grid)
  console.log(gridMap)
  console.log(Object.keys(grid).length)
}

function part2(instructions) {
  const { grid } = runRobotProgram(runProgram(instructions)(), 1)

  const gridMap = printGrid(grid)
  console.log(gridMap)
}

const input = require('fs')
  .readFileSync(require('path').resolve(__dirname, './input.txt'))
  .toString()

const instructions = input.split(',').map(Number)

part1(instructions)
part2(instructions)
Collapse
 
mustafahaddara profile image
Mustafa Haddara

I ranked on the global leaderboard! Never thought I'd make it. Came in at 87th for both stars and 83rd for the first star.

Ruby solution for part 2

class IntCode
    def initialize(instructions)
        @instructions = instructions
        @ptr = 0
        @in_buff = []
        @out_buff = []
        @rel_base = 0
        @blocked = false
    end

    def send_input(thing)
        @in_buff.push(thing)
    end

    def read_output()
        return @out_buff.shift()
    end

    def inspect()
        puts @instructions.to_s
        puts @out_buff.to_s
    end

    def isBlocked()
        return @blocked
    end

    def read_mem(idx)
        if idx < 0
            puts "wat: negative index"
        end
        val = @instructions[idx]
        if val == nil
            @instructions[idx] = 0
            return 0
        else
            return val
        end
    end

    def write_mem(idx)
    end

    def run()
        @blocked = false
        while @ptr < @instructions.length do
            # puts @ptr, @instructions.to_s
            base_op = read_mem(@ptr)
            # puts base_op
            op = base_op % 100  # get last 2 digits
            flags = base_op / 100 # top n digits
            arg1 = read_value(@instructions, @ptr+1, flags%10)
            if op == 1
                flags = flags / 10
                arg2 = read_value(@instructions, @ptr+2, flags%10)
                flags = flags / 10
                arg3 = read_value(@instructions, @ptr+3, flags%10)
                r = read_mem(arg1) + read_mem(arg2)
                @instructions[arg3] = r
                @ptr += 4
            elsif op == 2
                flags = flags / 10
                arg2 = read_value(@instructions, @ptr+2, flags%10)
                flags = flags / 10
                arg3 = read_value(@instructions, @ptr+3, flags%10)
                r = read_mem(arg1) * read_mem(arg2)
                @instructions[arg3] = r
                @ptr += 4
            elsif op == 3
                if @in_buff.empty?
                    # puts "waiting for input"
                    @blocked = true
                    return false
                end
                input = @in_buff.shift()
                # puts "got input #{input}, putting in location #{arg1}"
                @instructions[arg1] = input.to_i
                @ptr += 2
            elsif op == 4
                output = read_mem(arg1)
                @out_buff.push(output)
                # puts "output: #{output}"
                @ptr += 2
            elsif op == 5
                flags = flags / 10
                arg2 = read_value(@instructions, @ptr+2, flags%10)
                if read_mem(arg1) != 0
                    @ptr = read_mem(arg2)
                else
                    @ptr += 3
                end
            elsif op == 6
                flags = flags / 10
                arg2 = read_value(@instructions, @ptr+2, flags%10)
                if read_mem(arg1) == 0
                    @ptr = read_mem(arg2)
                else
                    @ptr += 3
                end
            elsif op == 7
                flags = flags / 10
                arg2 = read_value(@instructions, @ptr+2, flags%10)
                flags = flags / 10
                arg3 = read_value(@instructions, @ptr+3, flags%10)
                if read_mem(arg1) < read_mem(arg2)
                    @instructions[arg3] = 1
                else
                    @instructions[arg3] = 0
                end
                @ptr += 4
            elsif op == 8
                flags = flags / 10
                arg2 = read_value(@instructions, @ptr+2, flags%10)
                flags = flags / 10
                arg3 = read_value(@instructions, @ptr+3, flags%10)
                if read_mem(arg1) == read_mem(arg2)
                    @instructions[arg3] = 1
                else
                    @instructions[arg3] = 0
                end
                @ptr += 4
            elsif op == 9
                @rel_base += read_mem(arg1)
                # puts "updated relative base to #{@rel_base}"
                @ptr += 2
            elsif op == 99
                # puts "halting!"
                break
            else
                puts "wat"
                return @instructions
            end
        end
        return @instructions
    end

    def read_value(instructions, arg, flag)
        if flag == 1
            return arg
        elsif flag == 2
            v = read_mem(arg) + @rel_base
            return v
        else
            return read_mem(arg)
        end
    end
end

def paint(vm)
    grid = []

    grid[100] = []
    grid[100][100] = '#'

    # assume the bot starts at 100,100
    x = 100
    y = 100

    orientation = 'U'

    total_painted = 0

    while true
        if grid[y] == nil
            grid[y] = []
        end

        current_cell = grid[y][x]
        if current_cell == nil || current_cell == '.'
            current_cell = 0
        else
            current_cell = 1
        end
        vm.send_input(current_cell)

        vm.run()

        paint_color = vm.read_output()

        if grid[y][x] == nil
            total_painted += 1
        end

        if paint_color == 0
            grid[y][x] = '.'
        elsif paint_color == 1
            grid[y][x] = '#'
        else
            puts "wat"
        end

        new_dir = vm.read_output()
        orientation = rotate(orientation, new_dir)

        pair = move(x,y, orientation)
        x = pair[0]
        y = pair[1]

        if !vm.isBlocked
            break
        end
    end
    return grid
end

def rotate(orientation, rotate)
    if rotate == 0
        # left aka ccw
        if orientation == 'U'
            return 'L'
        elsif orientation == 'L'
            return 'D'
        elsif orientation == 'D'
            return 'R'
        elsif orientation == 'R'
            return 'U'
        end
    else
        # right aka cw
        if orientation == 'U'
            return 'R'
        elsif orientation == 'R'
            return 'D'
        elsif orientation == 'D'
            return 'L'
        elsif orientation == 'L'
            return 'U'
        end
    end
end

def move(x,y, dir)
    if dir == 'U'
        return [x, y-1]
    elsif dir == 'R'
        return [x+1, y]
    elsif dir == 'D'
        return [x, y+1]
    elsif dir == 'L'
        return [x-1, y]
    end
end

if __FILE__ == $0
    instructions = []
    File.open(ARGV[0], "r") do |file_handle|
        file_handle.each_line do |line|
            instructions = line.split(",").map { |n| n.to_i } 
            break
        end
    end


    vm = IntCode.new(instructions)
    g = paint(vm)
    g.each do |row|
        if row == nil
            next
        end
        # puts row.to_s
        row.each do |n|
            if n == nil || n == '.'
                print ' '
            else
                print n
            end
        end
        puts ""
    end
end
Collapse
 
rpalo profile image
Ryan Palo

Not too bad. Follow the steps: Input -> Process -> Output -> Repeat and keep track of the touched squares in a hashmap. Could have gone full-hog and made structs out of points and headings, but this makes sorting easier. Here's my Rust solution:

/// Day 11: Space Police
/// 
/// Repaint your ship to comply with the fuzz

use std::collections::HashMap;
use std::fs;

use crate::intcode::IntcodeInterpreter;

#[derive(Eq, PartialEq, PartialOrd, Ord, Debug)]
enum Color {
    White,
    Black,
}

/// A Position is a ordered pair (x, y) in 2D space.
type Position = (isize, isize);

/// A Heading is a direction vector (x, y) with y positive being up.
type Heading = (isize, isize);

/// Parses the input.  Expects a single line of integers separated by
/// commas
fn parse_input() -> Vec<isize> {
    let text: String = fs::read_to_string("data/day11.txt").unwrap();
    let cleaned = text.trim();
    cleaned.split(",").map( |c| c.parse().unwrap()).collect()
}

/// Turns right or left, returns the new heading
/// For direction, 1 means turn right, 0 means turn left
/// No need for a "clever" turning function I think.
fn turn(heading: Heading, direction: isize) -> Heading {
    if direction == 1 {
        match heading {
            (0, 1) => (1, 0),
            (1, 0) => (0, -1),
            (0, -1) => (-1, 0),
            (-1, 0) => (0, 1),
            _ => panic!("Unrecognized heading!")
        }
    } else {
        match heading {
            (0, 1) => (-1, 0),
            (-1, 0) => (0, -1),
            (0, -1) => (1, 0),
            (1, 0) => (0, 1),
            _ => panic!("Unrecognized heading!")
        }
    }
}

/// Paints a spacecraft hull using a painter bot that reads the current
/// tile color to decide what to do next
fn paint_hull(bot: &mut IntcodeInterpreter) -> HashMap<Position, Color> {
    let mut tiles: HashMap<Position, Color> = HashMap::new();
    let mut position = (0, 0);
    tiles.insert(position, Color::White);
    let mut heading = (0, 1);

    loop {
        // Read the tile color and pass it to the bot
        let input = match tiles.entry(position).or_insert(Color::Black) {
            Color::Black => 0,
            Color::White => 1,
        };
        bot.push_input(input);

        // Process input
        bot.run();

        // Read the color to paint and direction to turn from the bot
        let output_color = match bot.shift_output() {
            1 => Color::White,
            0 => Color::Black,
            _ => panic!("Bad bot color output")
        };
        let output_turn = bot.shift_output();

        tiles.insert(position, output_color);
        heading = turn(heading, output_turn);
        position = (position.0 + heading.0, position.1 + heading.1);

        // Return if the bot has halted completely
        if bot.current_instruction() == 99 {
            break;
        }
    }

    tiles
}

/// I cheated and printed the tiles map out to see the ranges of x and y
/// Prints black tiles as plain terminal and white as #
fn display_tiles(mut tiles: HashMap<Position, Color>) {
    for row in (-5..=0).rev() {
        for col in 0..42 {
            let out = match tiles.entry((col, row)).or_insert(Color::Black) {
                Color::White => "#",
                Color::Black => " ",
            };
            print!("{}", out);
        }
        println!("");
    }
}

pub fn run() {
    let mut bot = IntcodeInterpreter::new(parse_input());
    let tiles = paint_hull(&mut bot);
    println!("Touched {} squares", tiles.len());
    display_tiles(tiles);
}
Collapse
 
jbristow profile image
Jon Bristow

Phew! More dumb bugs slowed me down! However, once I figured out what was wrong, I was able to simplify my input/output functions a little but overall just use the intcode from the Amplifier day.

Starting to experiment more with lenses, they're neat, but seem a little unnecessary so far? I don't think I've seen the use-case that really calls for it. Like seeing a food processor next to a nice knife block; why wouldn't I just use a knife to chop things?

import arrow.core.Either
import arrow.core.None
import arrow.core.Some
import arrow.core.right
import arrow.optics.optics
import arrow.syntax.function.andThen
import intcode.CurrentState
import intcode.handleCodePoint
import java.nio.file.Files
import java.nio.file.Paths


object Day11 {
    private const val FILENAME = "src/main/resources/day11.txt"
    val fileData =
        Files.readAllLines(Paths.get(FILENAME)).first()
            .splitToSequence(",")
            .mapIndexed { i, it -> i.toLong() to it.toLong() }
            .toMap()
            .toMutableMap()

}

sealed class Direction {
    object Up : Direction()
    object Down : Direction()
    object Left : Direction()
    object Right : Direction()
}

val Direction.glyph: String
    get() = when (this) {
        is Direction.Up -> "⬆️"
        is Direction.Down -> "⬇️"
        is Direction.Left -> "⬅️"
        is Direction.Right -> "➡️"
    }


sealed class HullColor {
    object Black : HullColor()
    object White : HullColor()
}

private fun HullColor?.toInput(): Long {
    return when (this) {
        is HullColor.White -> 1L
        is HullColor.Black -> 0L
        else -> 0L
    }
}

@optics
data class Robot(
    val location: Point = Point(0, 0),
    val direction: Direction = Direction.Up,
    val state: Either<String, CurrentState> = CurrentState().right(),
    val code: MutableMap<Long, Long>,
    val hull: MutableMap<Point, HullColor> = mutableMapOf(),
    val bodyCounter: Int = 0,
    val brainCounter: Int = 0
) {
    companion object
}


fun Robot.turnLeft(): Robot = copy(
    direction = when (direction) {
        Direction.Up -> Direction.Left
        Direction.Down -> Direction.Right
        Direction.Left -> Direction.Down
        Direction.Right -> Direction.Up
    }
)

fun Robot.turnRight(): Robot = copy(
    direction = when (direction) {
        Direction.Up -> Direction.Right
        Direction.Down -> Direction.Left
        Direction.Left -> Direction.Up
        Direction.Right -> Direction.Down
    }
)

fun Robot.goForward(): Robot = copy(
    location = when (direction) {
        Direction.Up -> Point.y.set(location, location.y - 1)
        Direction.Down -> Point.y.set(location, location.y + 1)
        Direction.Left -> Point.x.set(location, location.x - 1)
        Direction.Right -> Point.x.set(location, location.x + 1)
    }
)

fun Robot.paint(instr: Long) = apply {
    hull[location] = when (instr) {
        0L -> HullColor.Black
        1L -> HullColor.White
        else -> throw Error("Illegal paint instruction: $instr")
    }
}

fun Robot.move(instr: Long): Robot = when (instr) {
    0L -> turnLeft()
    1L -> turnRight()
    else -> throw Error("Bad move instruction: $instr")
}.goForward()


tailrec fun Robot.bodyStep(): Robot = when {
    state is Either.Left<String> || state is Either.Right<CurrentState> && state.b.output.size < 2 -> this
    state is Either.Right<CurrentState> ->
        paint(state.b.output.pop())
            .move(state.b.output.pop())
            .copy(bodyCounter = bodyCounter + 1)
            .bodyStep()
    else -> throw Error("BodyStep Problem: $this")
}


private fun Either<String, CurrentState>.withUpdatedInputs(hullColor: HullColor?) =
    map {
        it.apply {
            if (waitingForInput) {
                inputs.add(hullColor.toInput())
            }
        }
    }

private fun Either<String, Robot>.fullOutput(): String {
    return fold({
        it
    }, {
        val topLeft = Point(it.hull.keys.map { p -> p.x }.min() ?: 0, it.hull.keys.map { p -> p.y }.min() ?: 0)
        val bottomRight = Point(it.hull.keys.map { p -> p.x }.max() ?: 0, it.hull.keys.map { p -> p.y }.max() ?: 0)
        return (topLeft.y..bottomRight.y).joinToString("\n") { y ->
            (topLeft.x..bottomRight.x).joinToString("") { x ->
                when {
                    Point(x, y) == it.location -> it.direction.glyph
                    it.hull[Point(x, y)] is HullColor.White -> "◽️"
                    it.hull[Point(x, y)] is HullColor.Black -> "◼️"
                    else -> "◼️"
                }
            }
        }

    })
}

fun Robot.brainStep(): Either<String, Robot> =
    when (state) {
        is Either.Left<String> -> state
        is Either.Right<CurrentState> -> when (state.b.pointer) {
            is None -> this.right()
            is Some<Long> -> {
                copy(
                    state = handleCodePoint(
                        code,
                        state.withUpdatedInputs(hull[location])
                    )
                    , brainCounter = brainCounter + 1
                ).bodyStep().brainStep()
            }
        }
    }

fun main() {
    println("Part 1")
    Robot(code = Day11.fileData).brainStep().let {
        println(it.map(Robot.hull::get andThen Map<Point, HullColor>::size))
        println(it.fullOutput())
    }

    println("\nPart 2")
    println(
        Robot(code = Day11.fileData, hull = mutableMapOf(Point(0, 0) to HullColor.White)).brainStep().fullOutput()
    )
}

Collapse
 
jbristow profile image
Jon Bristow
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◽️◼️◼️◽️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◼️◽️◽️◽️◼️◽️◼️◼️◽️◼️◼️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◽️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◽️◼️◽️◼️◽️◼️◼️◼️◼️◽️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️◽️◼️◽️◼️◼️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◼️◽️◽️◽️◼️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◼️◼️◽️◼️◽️◼️◽️◽️◼️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◼️◼️◽️◽️◼️◼️◽️◼️◽️◼️◽️◽️◼️◽️◽️◼️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◼️◽️◽️◽️◼️◽️◼️◼️◽️◽️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◽️◽️◽️◽️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◼️◼️◽️◼️◽️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◽️◼️◼️◽️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◽️◽️◽️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️◼️◽️◽️◽️◼️◼️◽️◼️◼️◼️◽️◼️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◽️◼️◽️◽️◽️◼️◼️◽️◽️◼️◽️◼️◽️◼️◽️◼️◽️◼️◽️◽️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◽️◼️◼️◽️◼️◼️◼️◽️◽️◽️◽️◼️◼️◽️◽️◼️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◽️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️◽️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◽️◽️◽️◽️◽️◼️◽️◼️◽️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◽️◼️◽️◼️◽️◼️◼️◽️◼️◽️◽️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◽️◼️◽️◼️◼️◽️◼️◼️◼️◼️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◽️◼️◼️◽️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◽️◽️◽️◽️◽️◼️◼️◽️◽️◼️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◽️◼️◽️◽️◽️◽️◽️◽️◼️◼️◼️◼️◽️◽️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◽️◼️◽️◼️◼️◽️◼️◽️◼️◽️◼️◽️◽️◼️◽️◽️◽️◽️◽️◽️◽️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◽️◼️◼️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◼️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◽️◽️◽️◼️◼️◽️◼️◽️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◽️◼️◼️◽️◽️◼️◽️◽️◽️◼️◽️◽️◽️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◽️◼️◼️◼️◽️◽️◽️◼️◼️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◽️◼️◽️◼️◽️◼️◼️◽️◽️◽️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◽️◽️◼️◽️◼️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◼️◼️◼️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◼️◽️◼️◽️◽️◽️◽️◼️◽️◼️◽️◽️◽️◽️◼️◽️◽️◼️◼️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◼️◼️◽️◼️◽️◽️◽️◼️◼️◽️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◽️◼️◽️◼️◽️◽️◼️◽️◽️◽️◼️◽️◽️◽️◽️◼️◼️◽️◽️◼️◽️◼️◽️◼️◼️◽️◼️◽️◽️◽️◼️◼️◽️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◽️◽️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◽️◼️◽️◼️◽️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◽️◽️◽️◽️◼️◽️◽️◼️◼️◽️◼️◼️◽️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◽️◽️◼️◼️◽️◼️◼️◽️◼️◽️◽️◼️◼️◽️◽️◽️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◽️◼️◽️◽️◼️◼️◽️◽️◼️◽️◼️◼️◽️◽️◼️◼️◽️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◽️◼️◼️◼️◽️◼️◽️◽️◽️◽️◽️◼️◼️◼️◽️◼️◼️◼️◽️◼️◼️◽️◽️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◼️◼️◽️◽️◼️◽️◼️◼️◽️◼️◼️◼️◽️◼️◼️◽️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◽️◼️◽️◽️◼️◽️◼️◽️◽️◽️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◼️◽️◼️◼️◽️◼️◽️◽️◽️◽️◼️◽️◼️◼️◽️◽️◽️◽️◽️◼️◼️◽️◼️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◼️◼️◼️◽️◽️◽️◽️◽️◽️◽️◽️◽️◼️◼️◽️◽️◽️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◼️◽️◼️◽️◽️◼️◼️◽️◽️◼️◽️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◼️◽️◼️◽️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◽️◽️◽️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️◽️◼️◼️◼️◽️◽️◽️◽️◽️◼️◼️◼️◽️◽️◼️◼️◼️◽️◽️◽️◼️◼️◽️◼️◼️◽️◼️◽️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◽️◽️◽️◼️◽️◽️◽️◼️◽️◼️◽️◽️◼️◽️◽️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◼️◼️◼️◽️◽️◼️◽️◼️◽️◼️◽️◼️◼️◼️◽️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◽️◽️◽️◼️◽️◽️◽️◽️◽️◽️◼️◽️◼️◼️◽️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◽️◽️◽️◽️◽️◼️◼️◽️◽️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◽️◽️◽️◼️◽️◼️◼️◽️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◼️◽️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◽️◽️◽️◽️◽️◽️◽️◼️◼️◽️◽️◽️◼️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️⬅️◼️◽️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◽️◽️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◽️◼️◽️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◽️◼️◼️◼️◽️◼️◽️◽️◽️◽️◽️◽️◽️◽️◽️◼️◽️◽️◼️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◽️◼️◽️◼️◼️◽️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◽️◽️◽️◼️◽️◼️◽️◼️◽️◽️◼️◽️◽️◽️◽️◽️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◽️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◽️◽️◽️◼️◼️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◽️◽️◽️◽️◽️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◽️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◼️◽️◽️◽️◼️◽️◼️◽️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◼️◽️◽️◽️◼️◼️◼️◽️◼️◼️◼️◼️◽️◽️◽️◼️◼️◼️◼️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◼️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◽️◽️◼️◽️◽️◼️◼️◼️◽️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◼️◽️◼️◼️◽️◼️◼️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◽️◼️◽️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◽️◼️◼️◼️◼️◽️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◽️◼️◽️◼️◼️◼️◼️◼️◽️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◽️◽️◼️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◽️◽️◼️◽️◼️◼️◽️◽️◼️◼️◽️◼️◽️◼️◼️◽️◼️◼️◽️◽️◼️◽️◽️◼️◽️◼️◽️◽️◼️◼️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◽️◼️◽️◽️◼️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◽️◽️◼️◼️◽️◽️◼️◽️◽️◼️◼️◼️◼️◽️◼️◽️◼️◼️◽️◽️◽️◽️◼️◽️◽️◽️◽️◼️◽️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◼️◼️◽️◽️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◽️◽️◼️◽️◽️◽️◼️◽️◼️◼️◽️◼️◽️◽️◽️◼️◽️◼️◼️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◽️◼️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◼️◼️◽️◽️◼️◼️◽️◼️◼️◽️◽️◽️◼️◼️◽️◽️◼️◼️◼️◽️◽️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◼️◼️◽️◽️◽️◽️◼️◽️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◽️◽️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◽️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◽️◼️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◽️◼️◼️◽️◼️◽️◼️◼️◽️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◽️◼️◽️◼️◼️◽️◼️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◽️◼️◼️◼️◽️◼️◽️◽️◼️◼️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◽️◼️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◽️◼️◼️◽️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◽️◼️◽️◽️◽️◼️◽️◽️◽️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◽️◼️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◽️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◽️◼️◼️◽️◽️◽️◽️◼️◽️◼️◽️◼️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◽️◽️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◽️◼️◼️◽️◽️◼️◼️◽️◼️◽️◽️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◽️◽️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◽️◽️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◼️◼️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◽️◼️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◽️◽️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️
Collapse
 
neilgall profile image
Neil Gall • Edited

Substantial refactor to my C IntCode machine for this one. I replaced the input/output buffers with function calls which call into the robot code. I also had the robot change the output function pointer on each paint/move, which seemed the simplest way to keep track of what to do on each output.

For part 2 I calculated the extent of painted panels and just visualised that rectangle. I needed a 200x100 grid to run my input program without running off the edge.

Despite a few segfaults in the early part of developing this one, I'm finding the C implementations of these problems the easiest, which is quite interesting as although I did my spell writing embedded firmware and Linux device drivers for a living 20 years ago, I haven't really done much C for ages.

Execution time today is 11ms.

Collapse
 
leonfedotov profile image
Leon Fedotov

not knowing size in advance made it tricky to print the result at first
github.com/LeonFedotov/advent-of-c...

Some comments may only be visible to logged-in visitors. Sign in to view all comments.