AOC第10天 - 阴极射线管
#go #adventofcode

阴极射线管

Question

废话似乎是我们昨天的绳索物理学知识没有帮助,我们在河里结束了...
精灵无处可寻,我们的通信系统吐出奇怪的信号和噪音
幸运的是,我们可能只需要立即启动并运行该设备,我们只需要对设备的视频系统进行替换。
为此,我们需要从CPU和寄存器值进行解码

我们获得了CPU输出,并告诉每个操作需要Y循环完成,例如:

noop
addx 3
addx -5
  • addx-进行两轮,将右手值添加到x
  • noop-一轮,什么都不做

解析

首先,创建一个新的Instruction struct

type Instruction struct {
  cycles int
  value  int
}

与前几天相比,这里的解析是一块蛋糕

func parse(raw string) (instructions []*Instruction) {
  lines := strings.Split(string(raw), "\n")

  for _, l := range lines {
    if strings.Contains(l, "noop") {
      instructions = append(instructions, &Instruction{cycles: 1, value: 0})
    } else {
      parts := strings.Split(l, " ")
      instructions = append(instructions, &Instruction{cycles: 2, value: util.ParseInt(parts[1])})
    }
  }
  return
}

您可能会注意到我们的函数签名有点怪异,它以(instructions []Instruction)结尾,并且在功能末尾什么也没返回。
该语法在我们函数的顶部创建一个变量,最后一个返回语句称为“裸”返回,默认情况下返回instructions
我认为,它不应用于具有超过几行代码的任何功能,而是应该使用命名变量和return instructions,但是为了学习新事物,我们将坚持使用“裸”返回(我有点喜欢那个术语)

第1部分

我们需要在各种CPU周期中的寄存器x*中进行采样,更精确地是在20th, 60th, 100th, 140th, 180th, and 220th周期期间

在此类问题中没有某种陷阱(通常),大多数模拟问题只需要仔细阅读,然后直接应用代码中的说明

直接来自AOC
addx V需要两个循环完成。 两个周期后,值V增加了X寄存器。(V可能为负。
noop需要一个周期来完成。它没有其他效果。

这里要注意的是,指令值仅在之后才生效特定的周期数已通过

那么我们的解决方案需要做什么?

  1. 遍历每个指令
  2. 跟踪系统ticks(与周期不同)
  3. 跟踪X
  4. 每个间隔中的x样品x
  5. 运行每个指令y周期数
  6. 每次指令后更新X

我们正在添加一个ticks的概念,每次运行中都会发生滴答

func Part1(raw string) int {
  instructions := parse(raw)
  x := 1
  result := 0
  ticks := 0
  for _, ci := range instructions {
    for j := 0; j < ci.cycles; j++ {
      ticks++ // updating ticks on every cycle
      if ticks%20 == 0 && ticks%40 != 0 {
        result += x * ticks
      }
    }
    x += ci.value
  }

  return result
}

第2部分

使用x的值在每个滴答过程中我们需要在屏幕上画一些东西
我们的屏幕宽40像素,其高度为6像素

没有垂直位置的概念,这意味着我们的x值需要翻译成我们在40*6

上定义的范围

我们被告知有一个sprite 3像素长,而x值决定了她的中心位置。
在每个周期中,我们在屏幕中碰到我们的位置,如果当前位置包括当前绘制的像素,我们说它 lit 并绘制#,否则它的 dark ,我们绘制.

输出应该是一系列字符,这将是我们的答案!那对吗?

这个问题比第1部分更棘手,但最主要的是仔细阅读说明并将其翻译回代码

首先,让我们创建一个屏幕!

func printCrt(crt [][]string) {
  for i, r := range crt {
    fmt.Println(i, r)
  }
}

func makeCrt() [][]string {
  crt := make([][]string, 6)
  for i, _ := range crt {
    crt[i] = make([]string, 40)
  }
  return crt
}

接下来是我们的逻辑

func Part2(raw string) {
  instructions := parse(raw)
  crt := makeCrt()
  x := 1
  ticks := 0
  for _, ci := range instructions {
    for j := 0; j < ci.cycles; j++ {
      row := int(ticks / 40)
      col := ticks % 40
      d := util.Abs(col - x)
      if d < 2 {
        crt[row][col] = "#"
      } else {
        crt[row][col] = "."
      }
      ticks++
    }
    x += ci.value
  }

  print(crt)
}

并非所有这里的一切都很明显,所以让我们逐一浏览棘手的行:

row := int(ticks / 40)
我们知道每行都是40行,因此我们可以将数字滴答除名每行的宽度以确定我们应该相对于CRT,例如30/40 -> 0, 90/40 -> 2等。


col := ticks%40
我们有一个长度为40的“窗口”,值正在增加,但我们仍然想落入该值的较小之中,例如30%40 -> 30, 50%40 -> 10 (second row 10th pixel), 220%40 -> 20等...


d := util.Abs(col - x)
我们的三角洲来自精灵的中心,如果它小于2(请记住x是精灵的中心),我们绘制了 lit 像素,否则我们绘制了 dark 像素

通过我的输入,我得到了以下输出,您呢?

advent of code day 10 output


就是今天,明天见,

您可以找到完整的代码here
感谢您的阅读!