2019 Complete!
This commit is contained in:
parent
fd5be536a3
commit
d6caa5a79d
264
2019/day25/cli_processor.go
Normal file
264
2019/day25/cli_processor.go
Normal file
@ -0,0 +1,264 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
type CliProc struct {
|
||||
History []string
|
||||
HistoryIdx int
|
||||
Suggestions []string // Suggestions are 'autocomplete' commands
|
||||
Proposals []string // Proposals are proposed 'next' commands
|
||||
|
||||
Buffer string
|
||||
Cursor int
|
||||
|
||||
Complete bool
|
||||
}
|
||||
|
||||
func NewCLI() *CliProc {
|
||||
c := CliProc{}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *CliProc) handleEvent(event termbox.Event) error {
|
||||
if c.Complete {
|
||||
return nil
|
||||
}
|
||||
if event.Ch == 0 {
|
||||
switch event.Key {
|
||||
case termbox.KeyEnter:
|
||||
// Execute command
|
||||
c.Complete = true
|
||||
|
||||
case termbox.KeySpace:
|
||||
c.putInBuffer(" ")
|
||||
|
||||
case termbox.KeyBackspace, termbox.KeyBackspace2:
|
||||
if c.Cursor > 0 {
|
||||
c.Buffer = c.Buffer[:c.Cursor-1] + c.Buffer[c.Cursor:]
|
||||
c.Cursor = c.Cursor - 1
|
||||
}
|
||||
case termbox.KeyDelete:
|
||||
if c.Cursor < len(c.Buffer) {
|
||||
c.Buffer = c.Buffer[:c.Cursor] + c.Buffer[c.Cursor+1:]
|
||||
}
|
||||
|
||||
case termbox.KeyArrowLeft:
|
||||
if c.Cursor > 0 {
|
||||
c.Cursor = c.Cursor - 1
|
||||
}
|
||||
case termbox.KeyArrowRight:
|
||||
if c.Cursor < len(c.Buffer) {
|
||||
c.Cursor = c.Cursor + 1
|
||||
} else {
|
||||
// Complete suggestion
|
||||
}
|
||||
case termbox.KeyHome:
|
||||
c.Cursor = 0
|
||||
case termbox.KeyEnd:
|
||||
c.Cursor = len(c.Buffer)
|
||||
|
||||
case termbox.KeyArrowUp:
|
||||
val, err := c.HistoryBack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Buffer = val
|
||||
c.Cursor = len(c.Buffer)
|
||||
|
||||
case termbox.KeyArrowDown:
|
||||
val, err := c.HistoryForward()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Buffer = val
|
||||
c.Cursor = len(c.Buffer)
|
||||
|
||||
case termbox.KeyTab:
|
||||
// Complete suggestion
|
||||
args := strings.Split(c.Buffer, " ")
|
||||
acText := strings.TrimPrefix(c.GetAutocompleteText(), args[len(args)-1])
|
||||
if len(acText) > 0 {
|
||||
c.Buffer = c.Buffer + acText
|
||||
c.Cursor = len(c.Buffer)
|
||||
}
|
||||
|
||||
case termbox.KeyCtrlU:
|
||||
// Delete everything from the cursor forward
|
||||
c.Buffer = c.Buffer[c.Cursor:]
|
||||
c.Cursor = 0
|
||||
}
|
||||
} else {
|
||||
c.putInBuffer(string(event.Ch))
|
||||
/*
|
||||
if c.Cursor == len(c.Buffer) {
|
||||
c.Buffer = c.Buffer + string(event.Ch)
|
||||
} else {
|
||||
c.Buffer = c.Buffer[:c.Cursor] + string(event.Ch) + c.Buffer[c.Cursor:]
|
||||
}
|
||||
c.Cursor = c.Cursor + 1
|
||||
*/
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CliProc) putInBuffer(v string) {
|
||||
c.Buffer = c.Buffer[:c.Cursor] + v + c.Buffer[c.Cursor:]
|
||||
c.Cursor = c.Cursor + 1
|
||||
}
|
||||
|
||||
func (c *CliProc) ClearBuffer() {
|
||||
c.Buffer = ""
|
||||
c.Cursor = 0
|
||||
c.Complete = false
|
||||
}
|
||||
|
||||
func (c *CliProc) GetBuffer() string {
|
||||
return c.Buffer
|
||||
}
|
||||
|
||||
func (c *CliProc) HistoryBack() (string, error) {
|
||||
if len(c.History) < c.HistoryIdx+1 {
|
||||
return "", errors.New("Already at oldest command")
|
||||
}
|
||||
c.HistoryIdx = c.HistoryIdx + 1
|
||||
return c.History[len(c.History)-c.HistoryIdx], nil
|
||||
}
|
||||
|
||||
func (c *CliProc) HistoryForward() (string, error) {
|
||||
if c.HistoryIdx == 0 {
|
||||
return "", errors.New("Already at most recent command")
|
||||
}
|
||||
c.HistoryIdx = c.HistoryIdx - 1
|
||||
if c.HistoryIdx == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return c.History[len(c.History)-c.HistoryIdx], nil
|
||||
}
|
||||
|
||||
func (c *CliProc) Suggest(cmd string) string {
|
||||
allSuggestions := c.GetAllSuggestions(cmd)
|
||||
if len(allSuggestions) == 0 {
|
||||
return ""
|
||||
}
|
||||
return allSuggestions[0]
|
||||
}
|
||||
|
||||
func (c *CliProc) GetDisplaySuggestions() []string {
|
||||
var ret []string
|
||||
v := c.GetAllSuggestions(c.Buffer)
|
||||
if len(c.Buffer) > 0 {
|
||||
for k := range v {
|
||||
if strings.HasPrefix(v[k], c.Buffer) {
|
||||
ret = append(ret, v[k])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normally, filter out 'drop's
|
||||
for k := range v {
|
||||
if !strings.HasPrefix(v[k], "drop ") {
|
||||
ret = append(ret, v[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *CliProc) GetAllSuggestions(cmd string) []string {
|
||||
var ret []string
|
||||
args := strings.Fields(cmd)
|
||||
if len(args) == 0 {
|
||||
// Return all commands
|
||||
for _, v := range c.Suggestions {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
return ret
|
||||
} else if len(args) == 1 && !strings.HasSuffix(cmd, " ") {
|
||||
for _, v := range c.Suggestions {
|
||||
if strings.HasPrefix(v, args[0]) {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *CliProc) Draw(x, y int, bg, fg termbox.Attribute) {
|
||||
DrawString("[ "+strings.Join(c.GetDisplaySuggestions(), ", ")+" ]", x, y, bg, fg)
|
||||
y = y + 1
|
||||
prompt := '>'
|
||||
termbox.SetCell(x, y, prompt, bg, fg)
|
||||
x = x + 2
|
||||
for k := range c.Buffer {
|
||||
useFg, useBg := bg, fg
|
||||
if k == c.Cursor {
|
||||
useFg, useBg = fg, bg
|
||||
}
|
||||
termbox.SetCell(x+k, y, rune(c.Buffer[k]), useFg, useBg)
|
||||
}
|
||||
args := strings.Split(c.Buffer, " ")
|
||||
acText := strings.TrimPrefix(c.GetAutocompleteText(), args[len(args)-1])
|
||||
if len(acText) > 0 {
|
||||
for k := range acText {
|
||||
useFg, useBg := bg|termbox.AttrBold, fg
|
||||
if k == 0 && c.Cursor == len(c.Buffer) {
|
||||
useFg, useBg = fg|termbox.AttrBold, bg
|
||||
}
|
||||
termbox.SetCell(x+len(c.Buffer)+k, y, rune(acText[k]), useFg, useBg)
|
||||
}
|
||||
} else {
|
||||
if c.Cursor == len(c.Buffer) {
|
||||
termbox.SetCell(x+len(c.Buffer), y, ' ', fg, bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CliProc) GetAutocompleteText() string {
|
||||
suggestions := c.GetAllSuggestions(c.Buffer)
|
||||
if len(suggestions) == 1 {
|
||||
args := strings.Split(c.Buffer, " ")
|
||||
return strings.TrimPrefix(suggestions[0], args[len(args)-1])
|
||||
} else if len(suggestions) > 1 {
|
||||
// Find how many characters we _can_ autocomplete
|
||||
var ret string
|
||||
for k := range suggestions[0] {
|
||||
allHave := true
|
||||
for wrk := range suggestions {
|
||||
if len(suggestions[wrk]) <= k || suggestions[0][k] != suggestions[wrk][k] {
|
||||
allHave = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allHave {
|
||||
ret = ret + string(suggestions[0][k])
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *CliProc) ClearSuggestions() {
|
||||
c.Suggestions = nil
|
||||
}
|
||||
|
||||
func (c *CliProc) AddSuggestion(s string) {
|
||||
c.AddSuggestions([]string{s})
|
||||
}
|
||||
func (c *CliProc) AddSuggestions(s []string) {
|
||||
c.Suggestions = append(c.Suggestions, s...)
|
||||
}
|
||||
|
||||
func (c *CliProc) RemoveSuggestion(s string) {
|
||||
var i int
|
||||
for i = range c.Suggestions {
|
||||
if c.Suggestions[i] == s {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.Suggestions = append(c.Suggestions[:i], c.Suggestions[i+1:]...)
|
||||
}
|
1382
2019/day25/debug-log
Normal file
1382
2019/day25/debug-log
Normal file
File diff suppressed because it is too large
Load Diff
175
2019/day25/event_buffer.go
Normal file
175
2019/day25/event_buffer.go
Normal file
@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultExpireTime = time.Second / 2
|
||||
)
|
||||
|
||||
type EventBuffer struct {
|
||||
UpdateTime time.Time
|
||||
TimeoutLen time.Duration
|
||||
Buffer []termbox.Event
|
||||
CursorPos int
|
||||
}
|
||||
|
||||
func NewEventBuffer() *EventBuffer {
|
||||
return &EventBuffer{
|
||||
TimeoutLen: DefaultExpireTime,
|
||||
CursorPos: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) checkExpiration() {
|
||||
if eb.TimeoutLen != -1 && time.Now().Sub(eb.UpdateTime) > eb.TimeoutLen {
|
||||
eb.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) StartsWith(r rune) bool {
|
||||
eb.checkExpiration()
|
||||
return len(eb.Buffer) > 0 && eb.Buffer[0].Ch == r
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) Add(event termbox.Event) {
|
||||
eb.checkExpiration()
|
||||
if event.Key == termbox.KeyCtrlU {
|
||||
eb.Clear()
|
||||
} else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
|
||||
eb.Backspace()
|
||||
} else {
|
||||
eb.Buffer = append(eb.Buffer, event)
|
||||
}
|
||||
eb.UpdateTime = time.Now()
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) Backspace() {
|
||||
eb.checkExpiration()
|
||||
if len(eb.Buffer) >= 1 {
|
||||
eb.Buffer = eb.Buffer[:len(eb.Buffer)-1]
|
||||
}
|
||||
eb.UpdateTime = time.Now()
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) MoveCursorBack() {
|
||||
if eb.CursorPos > 0 {
|
||||
eb.CursorPos--
|
||||
}
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) MoveCursorForward() {
|
||||
if eb.CursorPos < len(eb.Buffer) {
|
||||
eb.CursorPos++
|
||||
}
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) SetCursorToStart() {
|
||||
eb.CursorPos = 0
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) SetCursorToEnd() {
|
||||
eb.CursorPos = len(eb.Buffer)
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) Clear() {
|
||||
eb.Buffer = nil
|
||||
eb.UpdateTime = time.Now()
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) Events() []termbox.Event {
|
||||
eb.checkExpiration()
|
||||
return eb.Buffer
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) String() string {
|
||||
eb.checkExpiration()
|
||||
var ret string
|
||||
for _, v := range eb.Buffer {
|
||||
if v.Ch != 0 {
|
||||
ret = ret + string(v.Ch)
|
||||
} else {
|
||||
switch v.Key {
|
||||
case termbox.KeySpace:
|
||||
ret = ret + " "
|
||||
case termbox.KeyArrowUp:
|
||||
ret = ret + string(ChUpKeyValue)
|
||||
case termbox.KeyArrowRight:
|
||||
ret = ret + string(ChRightKeyValue)
|
||||
case termbox.KeyArrowDown:
|
||||
ret = ret + string(ChDownKeyValue)
|
||||
case termbox.KeyArrowLeft:
|
||||
ret = ret + string(ChLeftKeyValue)
|
||||
case termbox.KeyEnter:
|
||||
ret = ret + string(ChEnterKeyValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) SetToString(s string) {
|
||||
eb.Clear()
|
||||
for _, v := range s {
|
||||
if v == '\n' {
|
||||
eb.Add(termbox.Event{Ch: 0, Key: termbox.KeyEnter})
|
||||
} else {
|
||||
eb.Add(termbox.Event{Ch: v})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) MatchesString(s string) bool {
|
||||
eb.checkExpiration()
|
||||
if len(s) != len(eb.Buffer) {
|
||||
return false
|
||||
}
|
||||
return s == eb.String()
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) MatchesEvents(events []termbox.Event) bool {
|
||||
eb.checkExpiration()
|
||||
if len(events) != len(eb.Buffer) {
|
||||
return false
|
||||
}
|
||||
for k := range events {
|
||||
if events[k].Ch != eb.Buffer[k].Ch || events[k].Key != eb.Buffer[k].Key {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) StartsWithKey(key termbox.Key) bool {
|
||||
eb.checkExpiration()
|
||||
if len(eb.Buffer) > 0 {
|
||||
return eb.Buffer[0].Key == key
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) Size() int {
|
||||
eb.checkExpiration()
|
||||
return len(eb.Buffer)
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) IsNumber() bool {
|
||||
_, err := strconv.Atoi(eb.String())
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) Number() (int, error) {
|
||||
return strconv.Atoi(eb.String())
|
||||
}
|
||||
|
||||
func (eb *EventBuffer) OptNumber(def int) int {
|
||||
ret, err := strconv.Atoi(eb.String())
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
return ret
|
||||
}
|
1
2019/day25/input
Normal file
1
2019/day25/input
Normal file
File diff suppressed because one or more lines are too long
96
2019/day25/main.go
Normal file
96
2019/day25/main.go
Normal file
File diff suppressed because one or more lines are too long
122
2019/day25/map.txt
Normal file
122
2019/day25/map.txt
Normal file
@ -0,0 +1,122 @@
|
||||
9 5-6
|
||||
| | |
|
||||
8-4 7
|
||||
|
|
||||
1 |
|
||||
| |
|
||||
0-3
|
||||
| |
|
||||
2 |
|
||||
|
|
||||
C-B-A
|
||||
| |
|
||||
D E
|
||||
|
|
||||
F
|
||||
|
|
||||
H-G
|
||||
|
|
||||
I
|
||||
|
|
||||
*
|
||||
|
||||
0: Hull Breach ()
|
||||
1: Hallway (giant electromagnet -> prevents movement)
|
||||
2: Science Lab (astronaut ice cream)
|
||||
3: Passages (mouse)
|
||||
4: Arcade (spool of cat6)
|
||||
5: Engineering (hypercube)
|
||||
6: Crew Quarters (sand)
|
||||
7: Navigation (antenna)
|
||||
8: Observatory ()
|
||||
9: Stables (infinite loop -> Locks up game)
|
||||
A: Warp Drive Maintenance (mutex)
|
||||
B: Gift Wrapping Center (boulder)
|
||||
C: Sick Bay ()
|
||||
D: Holodeck (escape pod -> Launched into Space -> Exit)
|
||||
E: Kitchen ()
|
||||
F: Hot Chocolate Fountain (photons -> Eaten by a Grue)
|
||||
G: Corridor (molten lava)
|
||||
H: Storage ()
|
||||
I: Security Checkpoint ()
|
||||
*: GOAL!
|
||||
|
||||
# All Items:
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
sand
|
||||
antenna
|
||||
mutex
|
||||
boulder
|
||||
|
||||
# Entry Attempts:
|
||||
* 1: Too Light
|
||||
astronaut ice cream
|
||||
mouse
|
||||
mutex
|
||||
boulder
|
||||
|
||||
* 2: Too Heavy
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
sand
|
||||
antenna
|
||||
mutex
|
||||
boulder
|
||||
|
||||
* 3: Too Heavy
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
sand
|
||||
antenna
|
||||
|
||||
* 4: Too Heavy
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
sand
|
||||
|
||||
* 5: Too Light
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
|
||||
* 6: Too Heavy
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
sand
|
||||
|
||||
* 7: Too Light
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
|
||||
* 8: Too Heavy
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
antenna
|
||||
|
||||
* 9: Too Heavy
|
||||
mutex
|
||||
mouse
|
||||
spool of cat6
|
||||
hypercube
|
||||
antenna
|
||||
|
||||
* 10: Too Heavy
|
||||
astronaut ice cream
|
||||
mouse
|
||||
spool of cat6
|
||||
antenna
|
1
2019/day25/saveslot-quick.sav
Normal file
1
2019/day25/saveslot-quick.sav
Normal file
File diff suppressed because one or more lines are too long
404
2019/day25/screen.go
Normal file
404
2019/day25/screen.go
Normal file
@ -0,0 +1,404 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
intcode "git.bullercodeworks.com/brian/adventofcode/2019/intcode-processor"
|
||||
termboxScreen "github.com/br0xen/termbox-screen"
|
||||
termboxUtil "github.com/br0xen/termbox-util"
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
const (
|
||||
ModeWaiting = iota
|
||||
ModeInput
|
||||
)
|
||||
|
||||
const (
|
||||
Bg = termbox.ColorBlack
|
||||
Fg = termbox.ColorGreen
|
||||
)
|
||||
|
||||
var spinner []rune
|
||||
|
||||
type Screen struct {
|
||||
Title string
|
||||
Program *intcode.Program
|
||||
LastPI int
|
||||
Mode int
|
||||
Events *EventBuffer
|
||||
Cli *CliProc
|
||||
|
||||
TheLog []string
|
||||
LogStartIdx int
|
||||
LogDisplayLines int
|
||||
|
||||
StatusText string
|
||||
StatusSpinner int
|
||||
|
||||
Inventory []string
|
||||
|
||||
id int
|
||||
}
|
||||
|
||||
func NewScreen(p *intcode.Program) *Screen {
|
||||
s := Screen{
|
||||
Title: "Day 25: Cryostasis",
|
||||
Program: p,
|
||||
Mode: ModeWaiting,
|
||||
Events: NewEventBuffer(),
|
||||
Cli: NewCLI(),
|
||||
StatusSpinner: 0,
|
||||
}
|
||||
spinner = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▁'}
|
||||
|
||||
// Process to update screen from program
|
||||
go func() {
|
||||
for {
|
||||
if p.WaitingForInput || p.State == intcode.RET_DONE {
|
||||
if !s.Cli.Complete {
|
||||
s.Mode = ModeInput
|
||||
} else {
|
||||
s.Mode = ModeWaiting
|
||||
inp := s.Cli.GetBuffer()
|
||||
s.Cli.ClearBuffer()
|
||||
if s.IsEngineCommand(inp) {
|
||||
if err := s.ProcessCommand(inp); err != nil {
|
||||
s.Logln("Engine Error: " + err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
for k := range inp {
|
||||
for !p.WaitingForInput {
|
||||
time.Sleep(1)
|
||||
}
|
||||
s.StatusText = "Transmitting " + inp[0:k]
|
||||
s.Log(string(inp[k]))
|
||||
s.Program.Input(int(inp[k]))
|
||||
}
|
||||
s.StatusText = "Please Wait..."
|
||||
s.Program.Input(int('\n'))
|
||||
s.Logln("")
|
||||
if strings.HasPrefix(inp, "take ") {
|
||||
s.Cli.RemoveSuggestion(inp)
|
||||
invAdd := strings.TrimPrefix(inp, "take ")
|
||||
s.Inventory = append(s.Inventory, invAdd)
|
||||
} else if strings.HasPrefix(inp, "drop ") {
|
||||
invRem := strings.TrimPrefix(inp, "drop ")
|
||||
for i := range s.Inventory {
|
||||
if s.Inventory[i] == invRem {
|
||||
s.Inventory = append(s.Inventory[:i], s.Inventory[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if p.WaitingForOutput {
|
||||
v := s.Program.Output()
|
||||
s.StatusText = "Receiving '" + string(v) + "'"
|
||||
if v == '\n' {
|
||||
s.Logln("")
|
||||
} else {
|
||||
s.Log(string(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// And actually run the program
|
||||
s.Logln("Welcome to the Day25 Cryostasis Engine!")
|
||||
s.Logln("Type '/start' to start")
|
||||
s.Logln("Type '/load <save>' to load a save")
|
||||
go func() {
|
||||
ret := p.Run()
|
||||
if ret == intcode.RET_DONE {
|
||||
s.Logln("Program is done")
|
||||
} else if ret == intcode.RET_ERR {
|
||||
s.Logln("Program errored")
|
||||
}
|
||||
s.Logln("Type '/quit' to quit")
|
||||
}()
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *Screen) Id() int { return s.id }
|
||||
|
||||
func (s *Screen) Initialize(bundle termboxScreen.Bundle) error {
|
||||
s.Events.Clear()
|
||||
s.TheLog = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Screen) HandleNoneEvent(event termbox.Event) int {
|
||||
// Update the display
|
||||
return s.Id()
|
||||
}
|
||||
|
||||
func (s *Screen) HandleKeyEvent(event termbox.Event) int {
|
||||
switch event.Key {
|
||||
case termbox.KeyF5:
|
||||
if err := s.SaveState("quick"); err != nil {
|
||||
s.Logln("Engine Error: " + err.Error())
|
||||
} else {
|
||||
s.Logln("Quick Saved")
|
||||
}
|
||||
case termbox.KeyF7:
|
||||
if err := s.LoadState("quick"); err != nil {
|
||||
s.Logln("Engine Error: " + err.Error())
|
||||
} else {
|
||||
s.Logln("Quick Loaded")
|
||||
}
|
||||
case termbox.KeyPgup, termbox.KeyCtrlB:
|
||||
// Page down the log
|
||||
wrkLog := s.GetWrappedLog()
|
||||
if s.LogStartIdx < len(wrkLog)-s.LogDisplayLines {
|
||||
s.LogStartIdx = s.LogStartIdx + s.LogDisplayLines
|
||||
}
|
||||
if s.LogStartIdx > len(wrkLog)-s.LogDisplayLines {
|
||||
s.LogStartIdx = len(wrkLog) - s.LogDisplayLines
|
||||
}
|
||||
|
||||
case termbox.KeyPgdn, termbox.KeyCtrlF:
|
||||
// Page up the log
|
||||
if s.LogStartIdx > 0 {
|
||||
s.LogStartIdx = s.LogStartIdx - s.LogDisplayLines
|
||||
}
|
||||
if s.LogStartIdx < 0 {
|
||||
s.LogStartIdx = 0
|
||||
}
|
||||
default:
|
||||
if s.Mode == ModeInput {
|
||||
err := s.Cli.handleEvent(event)
|
||||
if err != nil {
|
||||
s.Log(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.Id()
|
||||
}
|
||||
|
||||
// ╭╼╾╮
|
||||
// │┼━│
|
||||
// ├┴┬┤
|
||||
// ╰──╯
|
||||
func (s *Screen) DrawScreen() {
|
||||
s.DrawHeader()
|
||||
s.DrawLog()
|
||||
s.DrawInventory()
|
||||
s.DrawTerminal()
|
||||
}
|
||||
|
||||
func (s *Screen) DrawLog() {
|
||||
w, h := termbox.Size()
|
||||
y := 1
|
||||
logW := w - 2
|
||||
h = h - 4
|
||||
s.LogDisplayLines = h - 1
|
||||
|
||||
wrkLog := s.GetWrappedLog()
|
||||
currStart := len(wrkLog) - s.LogDisplayLines - s.LogStartIdx
|
||||
if currStart < 0 {
|
||||
currStart = 0
|
||||
}
|
||||
statusStart := (len(wrkLog) - s.LogDisplayLines - currStart)
|
||||
if statusStart < 0 {
|
||||
statusStart = 0
|
||||
}
|
||||
logStatus := fmt.Sprintf("%d/%d (%d:%d)", statusStart, len(wrkLog), s.LogStartIdx, currStart)
|
||||
_, nY := termboxUtil.DrawStringAtPoint("╭"+logStatus+strings.Repeat("─", (logW-1-(len(logStatus))))+"╮", 1, y, Fg, Bg)
|
||||
|
||||
for len(wrkLog) < s.LogDisplayLines {
|
||||
wrkLog = append([]string{"~"}, wrkLog...)
|
||||
}
|
||||
for k := currStart; k < len(wrkLog) && k < currStart+s.LogDisplayLines; k++ {
|
||||
nY = nY + 1
|
||||
v := "~"
|
||||
if k >= 0 {
|
||||
v = wrkLog[k]
|
||||
}
|
||||
if len(v) > logW-4 {
|
||||
v = v[:logW-4]
|
||||
} else if len(v) < logW-4 {
|
||||
v = termboxUtil.AlignTextWithFill(v, logW-4, termboxUtil.AlignLeft, ' ')
|
||||
}
|
||||
_, nY = termboxUtil.DrawStringAtPoint(v, 2, nY, Fg, Bg)
|
||||
termbox.SetCell(1, nY, '│', Fg, Bg)
|
||||
termbox.SetCell((w - 1), nY, '│', Fg, Bg)
|
||||
}
|
||||
|
||||
termboxUtil.DrawStringAtPoint("├"+strings.Repeat("─", (logW-1))+"┤", 1, nY+1, Fg, Bg)
|
||||
}
|
||||
|
||||
func (s *Screen) DrawInventory() {
|
||||
w, _ := termbox.Size()
|
||||
title := "Inventory"
|
||||
longest := len(title)
|
||||
for k := range s.Inventory {
|
||||
if len(s.Inventory[k]) > longest {
|
||||
longest = len(s.Inventory[k])
|
||||
}
|
||||
}
|
||||
title = "┬" + title + strings.Repeat("─", longest-len(title)) + "╮"
|
||||
termboxUtil.DrawStringAtPoint(title, w-(longest+2), 1, Fg, Bg)
|
||||
for k := range s.Inventory {
|
||||
termboxUtil.DrawStringAtPoint(s.Inventory[k], w-(longest+1), 2+k, Fg, Bg)
|
||||
termbox.SetCell((w - longest - 2), 2+k, '│', Fg, Bg)
|
||||
termbox.SetCell((w - 1), 2+k, '│', Fg, Bg)
|
||||
}
|
||||
|
||||
termboxUtil.DrawStringAtPoint("╰"+strings.Repeat("─", len(title)-6)+"┤", w-(longest+2), 2+len(s.Inventory), Fg, Bg)
|
||||
}
|
||||
|
||||
func (s *Screen) DrawTerminal() {
|
||||
w, h := termbox.Size()
|
||||
termbox.SetCell(1, h-2, '│', Fg, Bg)
|
||||
termbox.SetCell((w - 1), h-2, '│', Fg, Bg)
|
||||
if s.Mode == ModeInput && !s.Cli.Complete {
|
||||
s.Cli.Draw(2, h-3, Fg, Bg)
|
||||
} else {
|
||||
s.DrawStatusLine()
|
||||
}
|
||||
termboxUtil.DrawStringAtPoint("╰"+strings.Repeat("─", (w-3))+"╯", 1, h-1, Fg, Bg)
|
||||
}
|
||||
|
||||
func (s *Screen) DrawStatusLine() {
|
||||
_, h := termbox.Size()
|
||||
if s.Program.Ptr != s.LastPI {
|
||||
s.StatusSpinner = (s.StatusSpinner + 1) % len(spinner)
|
||||
s.LastPI = s.Program.Ptr
|
||||
}
|
||||
status := string(spinner[s.StatusSpinner]) + " " + strconv.Itoa(s.LastPI)
|
||||
for len(status) < 10 {
|
||||
status = status + " "
|
||||
}
|
||||
termboxUtil.DrawStringAtPoint(status+" - "+s.StatusText, 2, h-2, Fg, Bg)
|
||||
}
|
||||
|
||||
func (s *Screen) DrawHeader() {
|
||||
width, _ := termbox.Size()
|
||||
spaces := strings.Repeat(" ", ((width-len(s.Title))/2)+1)
|
||||
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, s.Title, spaces), 0, 0, Bg, Fg)
|
||||
}
|
||||
|
||||
func (s *Screen) ResizeScreen() {
|
||||
s.Initialize(nil)
|
||||
}
|
||||
|
||||
func (s *Screen) GetWrappedLog() []string {
|
||||
w, _ := termbox.Size()
|
||||
var wrkLog []string
|
||||
for _, v := range s.TheLog {
|
||||
var line string
|
||||
pts := strings.Fields(v)
|
||||
for k := range pts {
|
||||
if len(line) == 0 {
|
||||
line = pts[k]
|
||||
} else {
|
||||
if len(line+" "+pts[k]) < w-4 {
|
||||
line = line + " " + pts[k]
|
||||
} else {
|
||||
wrkLog = append(wrkLog, line)
|
||||
line = pts[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
wrkLog = append(wrkLog, line)
|
||||
}
|
||||
return wrkLog
|
||||
}
|
||||
|
||||
func (s *Screen) Logln(msg string) {
|
||||
s.TheLog = append(s.TheLog, msg)
|
||||
}
|
||||
func (s *Screen) Log(msg string) {
|
||||
last := s.TheLog[len(s.TheLog)-1]
|
||||
last = last + msg
|
||||
s.TheLog[len(s.TheLog)-1] = last
|
||||
if last == "Command?" {
|
||||
s.ParseLogForCommands()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Screen) ParseLogForCommands() {
|
||||
// First fine the _previous_ 'Command?'
|
||||
s.Cli.ClearSuggestions()
|
||||
for k := len(s.TheLog) - 1; k >= 0; k-- {
|
||||
if strings.HasPrefix(s.TheLog[k], "== ") {
|
||||
// Room start
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(s.TheLog[k], "- ") {
|
||||
val := strings.TrimPrefix(s.TheLog[k], "- ")
|
||||
switch val {
|
||||
case "north", "east", "south", "west":
|
||||
s.Cli.AddSuggestion(val)
|
||||
default:
|
||||
var have bool
|
||||
for _, v := range s.Inventory {
|
||||
if v == val {
|
||||
have = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !have {
|
||||
s.Cli.AddSuggestion("take " + val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range s.Inventory {
|
||||
s.Cli.AddSuggestions([]string{"drop " + v})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Screen) IsEngineCommand(inp string) bool {
|
||||
return inp == "/quit" || strings.HasPrefix(inp, "/save") || strings.HasPrefix(inp, "/load")
|
||||
}
|
||||
func (s *Screen) ProcessCommand(inp string) error {
|
||||
if inp == "/quit" {
|
||||
s.id = -1
|
||||
} else if strings.HasPrefix(inp, "/save ") {
|
||||
pts := strings.Split(inp, " ")
|
||||
if len(pts) == 2 {
|
||||
return s.SaveState(pts[1])
|
||||
}
|
||||
return errors.New("No filename given")
|
||||
} else if strings.HasPrefix(inp, "/load ") {
|
||||
pts := strings.Split(inp, " ")
|
||||
if len(pts) == 2 {
|
||||
return s.LoadState(pts[1])
|
||||
}
|
||||
return errors.New("No filename given")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Screen) SaveState(slot string) error {
|
||||
// Save the engine state first
|
||||
f, err := os.OpenFile("saveslot-"+slot+".sav", os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
enc := json.NewEncoder(f)
|
||||
enc.Encode(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Screen) LoadState(slot string) error {
|
||||
hold, err := ioutil.ReadFile("saveslot-" + slot + ".sav")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json.NewDecoder(bytes.NewBuffer(hold)).Decode(&s)
|
||||
return nil
|
||||
}
|
33
2019/day25/util.go
Normal file
33
2019/day25/util.go
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
termboxUtil "github.com/br0xen/termbox-util"
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
// Some characters for display
|
||||
const (
|
||||
ChUpKeyValue = '↑'
|
||||
ChRightKeyValue = '→'
|
||||
ChDownKeyValue = '↓'
|
||||
ChLeftKeyValue = '←'
|
||||
ChEnterKeyValue = '⏎'
|
||||
ChCursor = '┇'
|
||||
)
|
||||
|
||||
func DrawString(v string, x, y int, fg, bg termbox.Attribute) (int, int) {
|
||||
termboxUtil.DrawStringAtPoint(v, x, y, fg, bg)
|
||||
return (x + len(v)), (y + 1)
|
||||
}
|
||||
|
||||
func FilterStrings(h []string, n string) []string {
|
||||
var ret []string
|
||||
for _, v := range h {
|
||||
if strings.HasPrefix(v, n) {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
118
2019/intcode-processor/opt_proc.go
Normal file
118
2019/intcode-processor/opt_proc.go
Normal file
@ -0,0 +1,118 @@
|
||||
package intcodeprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type OptProc struct {
|
||||
OriginalCode []int
|
||||
Code []int
|
||||
Ptr int
|
||||
RelBase int
|
||||
|
||||
State int
|
||||
Error error
|
||||
|
||||
Pause bool
|
||||
Bail bool
|
||||
WaitingForInput bool
|
||||
WaitingForOutput bool
|
||||
|
||||
Debug bool
|
||||
DebugToFile bool
|
||||
}
|
||||
|
||||
func NewOptProc(prog []int) *OptProc {
|
||||
p := new(OptProc)
|
||||
p.State = RET_OK
|
||||
p.OriginalCode = make([]int, len(prog))
|
||||
max := math.MaxInt16
|
||||
if 2*len(prog) > max {
|
||||
max = 2 * len(prog)
|
||||
}
|
||||
p.Code = make([]int, max)
|
||||
copy(p.OriginalCode, prog)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *OptProc) Run(in <-chan int, out chan<- int) int {
|
||||
p.Ptr = 0
|
||||
p.RelBase = 0
|
||||
copy(p.Code, p.OriginalCode)
|
||||
|
||||
for p.State == RET_OK {
|
||||
ins := fmt.Sprintf("%05d", p.Code[p.Ptr])
|
||||
opcode, _ := strconv.Atoi(ins[3:])
|
||||
arg := func(i int) int {
|
||||
switch ins[3-i] {
|
||||
case '1': // Immediate mode
|
||||
return p.Ptr + i
|
||||
case '2': // Relative mode
|
||||
return p.RelBase + p.Code[p.Ptr+i]
|
||||
default: // 1, position mode
|
||||
return p.Code[p.Ptr+i]
|
||||
}
|
||||
}
|
||||
switch opcode {
|
||||
case OP_ADD:
|
||||
p.Code[arg(3)] = p.Code[arg(1)] + p.Code[arg(2)]
|
||||
p.Ptr = p.Ptr + 4
|
||||
case OP_MLT:
|
||||
p.Code[arg(3)] = p.Code[arg(1)] * p.Code[arg(2)]
|
||||
p.Ptr += 4
|
||||
case OP_INP:
|
||||
p.WaitingForInput = true
|
||||
v := <-in
|
||||
p.WaitingForInput = false
|
||||
p.Code[arg(1)] = v
|
||||
p.Ptr += 2
|
||||
|
||||
case OP_OUT:
|
||||
p.WaitingForOutput = true
|
||||
out <- p.Code[arg(1)]
|
||||
p.WaitingForOutput = false
|
||||
p.Ptr += 2
|
||||
|
||||
case OP_JIT:
|
||||
if p.Code[arg(1)] != 0 {
|
||||
p.Ptr = p.Code[arg(2)]
|
||||
} else {
|
||||
p.Ptr += 3
|
||||
}
|
||||
case OP_JIF:
|
||||
if p.Code[arg(1)] == 0 {
|
||||
p.Ptr = p.Code[arg(2)]
|
||||
} else {
|
||||
p.Ptr += 3
|
||||
}
|
||||
case OP_ILT:
|
||||
if p.Code[arg(1)] < p.Code[arg(2)] {
|
||||
p.Code[arg(3)] = 1
|
||||
} else {
|
||||
p.Code[arg(3)] = 0
|
||||
}
|
||||
p.Ptr += 4
|
||||
case OP_IEQ:
|
||||
if p.Code[arg(1)] == p.Code[arg(2)] {
|
||||
p.Code[arg(3)] = 1
|
||||
} else {
|
||||
p.Code[arg(3)] = 0
|
||||
}
|
||||
p.Ptr += 4
|
||||
case OP_RBS:
|
||||
p.RelBase += p.Code[arg(1)]
|
||||
p.Ptr += 2
|
||||
case OP_EXT:
|
||||
p.State = RET_DONE
|
||||
default:
|
||||
p.State = RET_ERR
|
||||
}
|
||||
}
|
||||
return p.State
|
||||
}
|
||||
|
||||
func (p *OptProc) IsStopped() bool {
|
||||
return p.State != RET_OK
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
package intcodeprocessor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||||
)
|
||||
@ -37,21 +40,23 @@ const (
|
||||
)
|
||||
|
||||
type Program struct {
|
||||
originalCode []int
|
||||
code []int
|
||||
ptr int
|
||||
relBase int
|
||||
OriginalCode []int
|
||||
Code []int
|
||||
Ptr int
|
||||
RelBase int
|
||||
|
||||
state int
|
||||
error error
|
||||
State int
|
||||
Error error
|
||||
|
||||
bail bool
|
||||
waitingForInput bool
|
||||
input chan int
|
||||
waitingForOutput bool
|
||||
output chan int
|
||||
Pause bool
|
||||
Bail bool
|
||||
WaitingForInput bool
|
||||
InputChan chan int
|
||||
WaitingForOutput bool
|
||||
OutputChan chan int
|
||||
|
||||
debug bool
|
||||
Debug bool
|
||||
DebugToFile bool
|
||||
}
|
||||
|
||||
func ReadIntCodeFile(fn string) []int {
|
||||
@ -70,157 +75,180 @@ func ReadIntCodeFile(fn string) []int {
|
||||
|
||||
func NewProgram(prog []int) *Program {
|
||||
p := new(Program)
|
||||
p.originalCode = make([]int, len(prog))
|
||||
p.code = make([]int, len(prog))
|
||||
copy(p.originalCode, prog)
|
||||
p.OriginalCode = make([]int, len(prog))
|
||||
p.Code = make([]int, len(prog))
|
||||
copy(p.OriginalCode, prog)
|
||||
p.Reset()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Program) EnableDebug() {
|
||||
p.debug = true
|
||||
func (p *Program) SaveState(slot string) error {
|
||||
f, err := os.OpenFile("saveslot-"+slot+".sav", os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
enc := json.NewEncoder(f)
|
||||
enc.Encode(p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Program) DisableDebug() {
|
||||
p.debug = false
|
||||
func (p *Program) LoadState(slot string) error {
|
||||
hold, err := ioutil.ReadFile("saveslot-" + slot + ".sav")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json.NewDecoder(bytes.NewBuffer(hold)).Decode(&p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Program) Pointer() int {
|
||||
return p.Ptr
|
||||
}
|
||||
|
||||
func (p *Program) DebugLog(l string) {
|
||||
if p.debug {
|
||||
if p.Debug {
|
||||
fmt.Print(l)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) OutputToFile(l string) {
|
||||
f, err := os.OpenFile("debug-log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = fmt.Fprintln(f, l)
|
||||
}
|
||||
|
||||
func (p *Program) Reset() {
|
||||
copy(p.code, p.originalCode)
|
||||
p.ptr = 0
|
||||
p.state = RET_OK
|
||||
p.error = nil
|
||||
p.waitingForInput = false
|
||||
p.waitingForOutput = false
|
||||
p.input = make(chan int)
|
||||
p.output = make(chan int)
|
||||
p.relBase = 0
|
||||
}
|
||||
|
||||
func (p *Program) GetCode() []int {
|
||||
return p.code
|
||||
}
|
||||
|
||||
func (p *Program) State() int {
|
||||
return p.state
|
||||
}
|
||||
|
||||
func (p *Program) Error() error {
|
||||
return p.error
|
||||
copy(p.Code, p.OriginalCode)
|
||||
p.Ptr = 0
|
||||
p.State = RET_OK
|
||||
p.Error = nil
|
||||
p.Pause = false
|
||||
p.WaitingForInput = false
|
||||
p.WaitingForOutput = false
|
||||
p.InputChan = make(chan int)
|
||||
p.OutputChan = make(chan int)
|
||||
p.RelBase = 0
|
||||
}
|
||||
|
||||
func (p *Program) ForceQuit() {
|
||||
p.bail = true
|
||||
close(p.input)
|
||||
close(p.output)
|
||||
p.Bail = true
|
||||
close(p.InputChan)
|
||||
close(p.OutputChan)
|
||||
}
|
||||
|
||||
func (p *Program) Run() int {
|
||||
for {
|
||||
p.state = p.Step()
|
||||
if p.state != RET_OK {
|
||||
return p.state
|
||||
p.State = p.Step()
|
||||
if p.State != RET_OK {
|
||||
return p.State
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) Step() int {
|
||||
if p.bail {
|
||||
p.error = errors.New("Force Quit")
|
||||
for p.Pause {
|
||||
time.Sleep(1)
|
||||
}
|
||||
if p.Bail {
|
||||
p.Error = errors.New("Force Quit")
|
||||
return RET_ERR
|
||||
}
|
||||
if len(p.code) < p.ptr {
|
||||
p.error = errors.New("Pointer Exception")
|
||||
if len(p.Code) < p.Ptr {
|
||||
p.Error = errors.New("Pointer Exception")
|
||||
return RET_ERR
|
||||
}
|
||||
p.DebugLog(p.String() + "\n")
|
||||
intcode := p.readNext()
|
||||
p.ptr++
|
||||
p.Ptr++
|
||||
switch p.opCode(intcode) {
|
||||
case OP_ADD:
|
||||
v1, v2, v3 := p.readNextThree()
|
||||
p.DebugLog(fmt.Sprintf("ADD %d (%d, %d, %d)\n", intcode, v1, v2, v3))
|
||||
p.ptr = p.ptr + 3
|
||||
debug := fmt.Sprintf("ADD %d (%d, %d, %d)\n", intcode, v1, v2, v3)
|
||||
p.DebugLog(debug)
|
||||
if p.DebugToFile {
|
||||
p.OutputToFile(fmt.Sprintf("%d: %s", p.Ptr, debug))
|
||||
}
|
||||
p.Ptr = p.Ptr + 3
|
||||
p.opAdd(intcode, v1, v2, v3)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_MLT:
|
||||
v1, v2, v3 := p.readNextThree()
|
||||
p.DebugLog(fmt.Sprintf("MLT %d (%d, %d, %d)\n", intcode, v1, v2, v3))
|
||||
p.ptr = p.ptr + 3
|
||||
p.Ptr = p.Ptr + 3
|
||||
p.opMult(intcode, v1, v2, v3)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_INP:
|
||||
v1 := p.readNext()
|
||||
p.DebugLog(fmt.Sprintf("INP %d (%d)\n", intcode, v1))
|
||||
p.ptr++
|
||||
p.Ptr++
|
||||
p.opInp(intcode, v1)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_OUT:
|
||||
v1 := p.readNext()
|
||||
p.ptr++
|
||||
p.Ptr++
|
||||
p.DebugLog(fmt.Sprintf("OUT %d (%d)\n", intcode, v1))
|
||||
p.opOut(intcode, v1)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_JIT:
|
||||
v1, v2 := p.readNextTwo()
|
||||
p.DebugLog(fmt.Sprintf("JIT %d (%d, %d)\n", intcode, v1, v2))
|
||||
p.ptr = p.ptr + 2
|
||||
p.Ptr = p.Ptr + 2
|
||||
p.opJumpIfTrue(intcode, v1, v2)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_JIF:
|
||||
v1, v2 := p.readNextTwo()
|
||||
p.DebugLog(fmt.Sprintf("JIF %d (%d, %d)\n", intcode, v1, v2))
|
||||
p.ptr = p.ptr + 2
|
||||
p.Ptr = p.Ptr + 2
|
||||
p.opJumpIfFalse(intcode, v1, v2)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_ILT:
|
||||
v1, v2, dest := p.readNextThree()
|
||||
p.DebugLog(fmt.Sprintf("ILT %d (%d, %d, %d)\n", intcode, v1, v2, dest))
|
||||
p.ptr = p.ptr + 3
|
||||
p.Ptr = p.Ptr + 3
|
||||
p.opIfLessThan(intcode, v1, v2, dest)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_IEQ:
|
||||
v1, v2, dest := p.readNextThree()
|
||||
p.DebugLog(fmt.Sprintf("IEQ %d (%d, %d, %d)\n", intcode, v1, v2, dest))
|
||||
p.ptr = p.ptr + 3
|
||||
debug := fmt.Sprintf("IEQ %d (%d, %d, %d)\n", intcode, v1, v2, dest)
|
||||
p.DebugLog(debug)
|
||||
p.Ptr = p.Ptr + 3
|
||||
p.opIfEqual(intcode, v1, v2, dest)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
case OP_RBS:
|
||||
v1 := p.readNext()
|
||||
p.DebugLog(fmt.Sprintf("RBS %d (%d)\n", intcode, v1))
|
||||
p.ptr = p.ptr + 1
|
||||
p.Ptr = p.Ptr + 1
|
||||
p.opModRelBase(intcode, v1)
|
||||
if p.error != nil {
|
||||
if p.Error != nil {
|
||||
return RET_ERR
|
||||
}
|
||||
return RET_OK
|
||||
@ -229,31 +257,31 @@ func (p *Program) Step() int {
|
||||
p.DebugLog(fmt.Sprintf("EXT %d\n", intcode))
|
||||
return RET_DONE
|
||||
}
|
||||
p.error = errors.New(fmt.Sprintf("Invalid OpCode (%d)", intcode))
|
||||
p.Error = errors.New(fmt.Sprintf("Invalid OpCode (%d)", intcode))
|
||||
p.DebugLog(p.String())
|
||||
return RET_ERR
|
||||
}
|
||||
|
||||
func (p *Program) GetCurrentOpCode() int {
|
||||
return p.code[p.ptr]
|
||||
return p.Code[p.Ptr]
|
||||
}
|
||||
|
||||
func (p *Program) GetProgramValueAt(idx int) int {
|
||||
p.ensureLength(idx)
|
||||
return p.code[idx]
|
||||
return p.Code[idx]
|
||||
}
|
||||
|
||||
func (p *Program) SetProgramValueAt(idx, val int) {
|
||||
p.ensureLength(idx)
|
||||
p.code[idx] = val
|
||||
p.Code[idx] = val
|
||||
}
|
||||
|
||||
func (p *Program) NeedsInput() bool {
|
||||
return p.waitingForInput
|
||||
return p.WaitingForInput
|
||||
}
|
||||
|
||||
func (p *Program) NeedsOutput() bool {
|
||||
return p.waitingForOutput
|
||||
return p.WaitingForOutput
|
||||
}
|
||||
|
||||
func (p *Program) opCode(intcode int) int {
|
||||
@ -266,33 +294,33 @@ func (p *Program) paramMode(intcode, pNum int) int {
|
||||
}
|
||||
|
||||
func (p *Program) ensureLength(idx int) {
|
||||
for len(p.code) < idx+1 {
|
||||
p.code = append(p.code, 0)
|
||||
for len(p.Code) < idx+1 {
|
||||
p.Code = append(p.Code, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) readNext() int {
|
||||
p.ensureLength(p.ptr)
|
||||
return p.code[p.ptr]
|
||||
p.ensureLength(p.Ptr)
|
||||
return p.Code[p.Ptr]
|
||||
}
|
||||
|
||||
func (p *Program) readNextTwo() (int, int) {
|
||||
p.ensureLength(p.ptr + 1)
|
||||
return p.code[p.ptr], p.code[p.ptr+1]
|
||||
p.ensureLength(p.Ptr + 1)
|
||||
return p.Code[p.Ptr], p.Code[p.Ptr+1]
|
||||
}
|
||||
|
||||
func (p *Program) readNextThree() (int, int, int) {
|
||||
p.ensureLength(p.ptr + 2)
|
||||
return p.code[p.ptr], p.code[p.ptr+1], p.code[p.ptr+2]
|
||||
p.ensureLength(p.Ptr + 2)
|
||||
return p.Code[p.Ptr], p.Code[p.Ptr+1], p.Code[p.Ptr+2]
|
||||
}
|
||||
|
||||
func (p *Program) get(mode, v int) int {
|
||||
if mode == MODE_POS {
|
||||
p.ensureLength(v)
|
||||
return p.code[v]
|
||||
return p.Code[v]
|
||||
} else if mode == MODE_REL {
|
||||
p.ensureLength(p.relBase + v)
|
||||
return p.code[p.relBase+v]
|
||||
p.ensureLength(p.RelBase + v)
|
||||
return p.Code[p.RelBase+v]
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -300,21 +328,21 @@ func (p *Program) get(mode, v int) int {
|
||||
func (p *Program) set(mode, idx, v int) {
|
||||
if mode == MODE_POS {
|
||||
p.ensureLength(idx)
|
||||
p.code[idx] = v
|
||||
p.Code[idx] = v
|
||||
} else if mode == MODE_REL {
|
||||
p.ensureLength(p.relBase + idx)
|
||||
p.code[p.relBase+idx] = v
|
||||
p.ensureLength(p.RelBase + idx)
|
||||
p.Code[p.RelBase+idx] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) Input(v int) {
|
||||
p.input <- v
|
||||
//p.waitingForInput = false
|
||||
p.InputChan <- v
|
||||
//p.WaitingForInput = false
|
||||
}
|
||||
|
||||
func (p *Program) Output() int {
|
||||
v := <-p.output
|
||||
p.waitingForOutput = false
|
||||
v := <-p.OutputChan
|
||||
p.WaitingForOutput = false
|
||||
return v
|
||||
}
|
||||
|
||||
@ -330,29 +358,29 @@ func (p *Program) opMult(intcode, a1, a2, dest int) {
|
||||
|
||||
func (p *Program) opInp(intcode, dest int) {
|
||||
destmd := p.paramMode(intcode, 0)
|
||||
p.waitingForInput = true
|
||||
p.set(destmd, dest, <-p.input)
|
||||
p.waitingForInput = false
|
||||
p.WaitingForInput = true
|
||||
p.set(destmd, dest, <-p.InputChan)
|
||||
p.WaitingForInput = false
|
||||
}
|
||||
|
||||
func (p *Program) opOut(intcode, val int) {
|
||||
valmd := p.paramMode(intcode, 0)
|
||||
ret := p.get(valmd, val)
|
||||
p.waitingForOutput = true
|
||||
p.output <- ret
|
||||
p.WaitingForOutput = true
|
||||
p.OutputChan <- ret
|
||||
}
|
||||
|
||||
func (p *Program) opJumpIfTrue(intcode, v1, v2 int) {
|
||||
v1md, v2md := p.paramMode(intcode, 0), p.paramMode(intcode, 1)
|
||||
if p.get(v1md, v1) != 0 {
|
||||
p.ptr = p.get(v2md, v2)
|
||||
p.Ptr = p.get(v2md, v2)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) opJumpIfFalse(intcode, v1, v2 int) {
|
||||
v1md, v2md := p.paramMode(intcode, 0), p.paramMode(intcode, 1)
|
||||
if p.get(v1md, v1) == 0 {
|
||||
p.ptr = p.get(v2md, v2)
|
||||
p.Ptr = p.get(v2md, v2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,17 +404,17 @@ func (p *Program) opIfEqual(intcode, v1, v2, dest int) {
|
||||
|
||||
func (p *Program) opModRelBase(intcode, v1 int) {
|
||||
v1md := p.paramMode(intcode, 0)
|
||||
p.relBase = p.relBase + p.get(v1md, v1)
|
||||
p.RelBase = p.RelBase + p.get(v1md, v1)
|
||||
}
|
||||
|
||||
func (p Program) String() string {
|
||||
var ret string
|
||||
ret = ret + fmt.Sprintf("(PTR: %d, RBS: %d)\n", p.ptr, p.relBase)
|
||||
for k := range p.code {
|
||||
if k == p.ptr {
|
||||
ret = fmt.Sprintf("%s [%d]", ret, p.code[k])
|
||||
ret = ret + fmt.Sprintf("(PTR: %d, RBS: %d)\n", p.Ptr, p.RelBase)
|
||||
for k := range p.Code {
|
||||
if k == p.Ptr {
|
||||
ret = fmt.Sprintf("%s [%d]", ret, p.Code[k])
|
||||
} else {
|
||||
ret = fmt.Sprintf("%s %d", ret, p.code[k])
|
||||
ret = fmt.Sprintf("%s %d", ret, p.Code[k])
|
||||
}
|
||||
}
|
||||
return ret
|
||||
|
Loading…
Reference in New Issue
Block a user