Building a Computer in R

Gus Lipkin

About Me

  • At work:
    • I love R
    • Data scientist at Lander Analytics
    • BS in Business Analytics

About Me

  • Not at work:
    • Mentor in the Data Science Learning Community
      • Talk to Jon Harmon to learn more!
    • Hiking, biking, rock climbing
      • Volunteer at USA Climbing events
    • Work on random projects

What are we doing?

  • “R is a language and environment for statistical computing and graphics” The R Project
  • What if we ignored that and made a computer?

What is a computer?

  • A computer can:
    • Store data
    • Retrieve data
    • “Carry out sequences of arithmetic or logical operations” Wikipedia (It’s a great source!)

Why?

  • Advent of Code!
    • 25 programming puzzles to save Christmas

What is our puzzle?

  • Follow these instructions to get a secret phrase that is the solution to this year’s puzzle to help save NYR 10.
  • You have five registers that start with a value of "0".
  • Because the computer can only process integers, the characters are stored in UTF8 as integers.
  • Each character is stored in a register a through e.
  • The instructions are add and sub, which add to a register and assign it back to that register and or subtract a value from that register’s value and assign it back to that register.

What are our instructions?

readLines(ins_txt) |>
  paste0(collapse = '\n') |>
  cat()

What are our instructions?

readLines(ins_txt) |>
  paste0(collapse = '\n') |>
  cat()
add b 8
sub e 29
add d 1
sub d 16
sub a 21
add b 28
add b 5
add d 16
add a 10
add c 61
add e 14
sub c 15
add a 41
add e 15
sub c 12

Making a plan

  • Parse the instructions from a text file
  • An R object that is self-referenceable
    • A list of registers
    • A list of instructions
    • A way to step through the instructions in order

Processing Our Instructions

  • Read in the file
readLines(ins_txt)
 [1] "add b 8"  "sub e 29" "add d 1"  "sub d 16" "sub a 21" "add b 28"
 [7] "add b 5"  "add d 16" "add a 10" "add c 61" "add e 14" "sub c 15"
[13] "add a 41" "add e 15" "sub c 12"

Processing Our Instructions

  • Split each line into words
readLines(ins_txt) |>
  stringr::str_split(' ')
[[1]]
[1] "add" "b"   "8"  

[[2]]
[1] "sub" "e"   "29" 

[[3]]
[1] "add" "d"   "1"  

[[4]]
[1] "sub" "d"   "16" 

[[5]]
[1] "sub" "a"   "21" 

[[6]]
[1] "add" "b"   "28" 

[[7]]
[1] "add" "b"   "5"  

[[8]]
[1] "add" "d"   "16" 

[[9]]
[1] "add" "a"   "10" 

[[10]]
[1] "add" "c"   "61" 

[[11]]
[1] "add" "e"   "14" 

[[12]]
[1] "sub" "c"   "15" 

[[13]]
[1] "add" "a"   "41" 

[[14]]
[1] "add" "e"   "15" 

[[15]]
[1] "sub" "c"   "12" 

Processing Our Instructions

  • Name the function, register, and yalues
instructions <-
  readLines(ins_txt) |>
  stringr::str_split(' ') |>
  purrr::map(\(x) { names(x) <- c('f', 'r', 'y'); return(x); }) |>
  print()
[[1]]
    f     r     y 
"add"   "b"   "8" 

[[2]]
    f     r     y 
"sub"   "e"  "29" 

[[3]]
    f     r     y 
"add"   "d"   "1" 

[[4]]
    f     r     y 
"sub"   "d"  "16" 

[[5]]
    f     r     y 
"sub"   "a"  "21" 

[[6]]
    f     r     y 
"add"   "b"  "28" 

[[7]]
    f     r     y 
"add"   "b"   "5" 

[[8]]
    f     r     y 
"add"   "d"  "16" 

[[9]]
    f     r     y 
"add"   "a"  "10" 

[[10]]
    f     r     y 
"add"   "c"  "61" 

[[11]]
    f     r     y 
"add"   "e"  "14" 

[[12]]
    f     r     y 
"sub"   "c"  "15" 

[[13]]
    f     r     y 
"add"   "a"  "41" 

[[14]]
    f     r     y 
"add"   "e"  "15" 

[[15]]
    f     r     y 
"sub"   "c"  "12" 

Choosing an OOP System

Starting with an R6 object

  • Can modify itself using the self object
  • Provides public and private methods (functions)

Starting with an R6 object

  • Can modify itself using the self object
  • Provides public and private methods (functions)
R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(),
  'private' = list()
)
<nyr10> object generator
  Public:
    clone: function (deep = FALSE) 
  Parent env: <environment: R_GlobalEnv>
  Locked objects: TRUE
  Locked class: FALSE
  Portable: TRUE

Creating Registers

  • You have five registers that start with a value of "0".
  • Because the computer can only process integers, the characters are stored in UTF8 as integers.
  • Each character is stored in a register a through e.

Creating Registers

  • You have five registers that start with a value of "0".
  • Because the computer can only process integers, the characters are stored in UTF8 as integers.
  • Each character is stored in a register a through e.
(zeroAsUTF8 <- utf8ToInt('0'))
[1] 48

Creating Registers

  • You have five registers that start with a value of "0".
  • Because the computer can only process integers, the characters are stored in UTF8 as integers.
  • Each character is stored in a register a through e.
zeroAsUTF8 <- utf8ToInt('0')
registers <- 
  list(
    'a' = zeroAsUTF8,
    'b' = zeroAsUTF8,
    'c' = zeroAsUTF8,
    'd' = zeroAsUTF8,
    'e' = zeroAsUTF8
  )

Creating Functions

  • The instructions are add and sub, which add to a register and assign it back to that register and or subtract a value from that register’s value and assign it back to that register.

Creating Functions

  • The instructions are add and sub, which add to a register and assign it back to that register and or subtract a value from that register’s value and assign it back to that register.
functions <-
  list(
    'add' = \(r, y) { self[[r]] <- self[[r]] + y },
    'sub' = \(r, y) { self[[r]] <- self[[r]] - y }
  )

Creating an Empty Computer

R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(),
  'private' = list()
)

Adding a System Clock

R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(),
  'private' = list()
)
  • We need an index for the clock
  • It needs to increment

Adding a System Clock

  • We need an index for the clock
  • It needs to increment
R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(
    'index' = 1
  ),
  'private' = list(
    '.inc' = \() { self$index <- self$index + 1; return(self); }
  )
)

Calling Our Functions

R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(
    'index' = 1
  ),
  'private' = list(
    '.inc' = \() { self$index <- self$index + 1; return(self); }
  )
)
  • We need a way to call our functions
  • We’ll increment our clock after each function

Calling Our Functions

  • We need a way to call our functions
  • We’ll increment our clock after each function
R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(
    ...,
    'call' = \(fun, reg, y) {
      self[[fun]](reg, y)
      private$.inc()
    }
  ),
  'private' = list(
    ...
  )
)

Running Through the Instructions

R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(
    'index' = 1,
    'call' = \(fun, reg, y) {
      self[[fun]](reg, y)
      private$.inc()
    }
  ),
  'private' = list(
    '.inc' = \() { self$index <- self$index + 1; return(self); }
  )
)
  • We need to step through our instructions
  • Return the computer’s state when we reach the end of the list

Running Through the Instructions

  • We need to step through our instructions
  • Return the computer’s state when we reach the end of the list
R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(
    ...,
    'run' = \(insructions) {
      while(self$index <= length(insructions)) { 
        ins <- instructions[[self$index]]
        self$call(ins['f'], ins['r'], as.integer(ins['y']))
      }
      return(self)
    }
  ),
  'private' = list(
    ...
  )
)

Adding in Our Registers and Functions

R6::R6Class(
  'classname' = 'nyr10',
  'public' = list(
    'index' = 1,
    'call' = \(fun, reg, y) {
      self[[fun]](reg, y)
      private$.inc()
    },
    'run' = \(insructions) {
      while(self$index <= length(insructions)) { 
        ins <- instructions[[self$index]]
        self$call(ins['f'], ins['r'], as.integer(ins['y']))
      }
      return(self)
    }
  ),
  'private' = list(
    '.inc' = \() { self$index <- self$index + 1; return(self); }
  )
)
  • We need to add our registers and functions

Adding in Our Registers and Functions

  • We need to add our registers and functions
R6::R6Class(
  'nyr10',
  'public' = unlist(list(
    as.list(c(registers, functions)),
    ...
  )),
  'private' = list(
    ...
  )
)

Building the Computer Class

computer <- R6::R6Class(
  'nyr10',
  'public' = unlist(list(
    as.list(c(registers, functions)),
    'index' = 1,
    'call' = \(fun, reg, y) {
      self[[fun]](reg, y)
      private$.inc()
    },
    'run' = \(insructions) {
      while(self$index <= length(insructions)) { 
        ins <- instructions[[self$index]]
        self$call(ins['f'], ins['r'], as.integer(ins['y']))
      }
      return(self)
    }
  )),
  'private' = list(
    '.inc' = \() { self$index <- self$index + 1; return(self); }
  )
)

Creating a Computer

Creating a Computer

comp <- computer$new()

Creating a Computer

comp <- computer$new()
print(comp)
<nyr10>
  Public:
    a: 48
    add: function (r, y) 
    b: 48
    c: 48
    call: function (fun, reg, y) 
    clone: function (deep = FALSE) 
    d: 48
    e: 48
    index: 1
    run: function (insructions) 
    sub: function (r, y) 
  Private:
    .inc: function () 

Running the Instructions

end <- comp$run(instructions)

Running the Instructions

end <- comp$run(instructions)
print(end)
<nyr10>
  Public:
    a: 78
    add: function (r, y) 
    b: 89
    c: 82
    call: function (fun, reg, y) 
    clone: function (deep = FALSE) 
    d: 49
    e: 48
    index: 16
    run: function (insructions) 
    sub: function (r, y) 
  Private:
    .inc: function () 

Translating the Secret Message

letters[1:5] |>
  purrr::map_chr(\(l) intToUtf8(end[[l]])) |>
  paste0(collapse = '') |>
  cat()

Translating the Secret Message

letters[1:5] |>
  purrr::map_chr(\(l) intToUtf8(end[[l]])) |>
  paste0(collapse = '') |>
  cat()
NYR10

Where to Find Me

  • Upstairs!
  • Online at guslipkin.me
    • Links to everywhere else from there