package door import ( "bytes" "fmt" "log" "time" ) /* WOPR [ GAME ] [ TIME ELAPSED ] [ XX HRS ] [ XX MIN XX SEC ] [■ ] -> [ GAME ] [ TIME REMAINING ] [ XX HRS ] [ XX MIN XX SEC ] [ ■] <- Border SINGLE Ok, Width = 16 1000 / 16 = 62.5 ms = 62500 us Width 16 (at pos 1, update time/sec increment) Black on Cyan */ type WOPR struct { ElapsedPanel Panel Elapsed time.Time ElapsedD time.Duration RemainingPanel Panel Remaining time.Time RemainingD time.Duration Color string Index int Ticker *time.Ticker StopIt chan bool output *bytes.Buffer } const DEBUG_WOPR bool = true func (w *WOPR) Clear() []byte { var output bytes.Buffer output.Write(w.ElapsedPanel.Clear()) output.Write(w.RemainingPanel.Clear()) return output.Bytes() } // Initialize, Set X, Y on Panels, Animate() // Initialize, and create panels func (w *WOPR) Init(elapsed time.Time, remaining time.Time, color []byte) { if len(color) == 0 { color = ColorText("BLACK ON CYAN") } w.output = &bytes.Buffer{} w.Elapsed = elapsed w.Remaining = remaining w.Index = 0 w.ElapsedPanel = Panel{Width: 16, BorderColor: color, Style: SINGLE} w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, NewLine(" GAME ")) w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, NewLine(" TIME ELAPSED ")) var ehour *Line = &Line{} ehour.UpdateF = func(u *bytes.Buffer) { var hours int = int(w.ElapsedD.Hours()) u.Reset() /* // This does not change anything in the benchmark. u.WriteString(" ") u.WriteByte(byte(int('0') + (hours%100)/10)) u.WriteByte(byte(int('0') + (hours%100)%10)) u.WriteString(" HRS ") */ fmt.Fprintf(u, " %02d HRS ", hours%100) } ehour.Update() // ehour.Text.Write(ehour.update) w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, ehour) var eminsec *Line = &Line{} eminsec.UpdateF = func(u *bytes.Buffer) { var mins int = int(w.ElapsedD.Minutes()) % 60 var secs int = int(w.ElapsedD.Seconds()) % 60 u.Reset() fmt.Fprintf(u, " %02d MIN %02d SEC ", mins, secs) } eminsec.Update() // Text.Write(eminsec.UpdateF()) w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, eminsec) var eanimate *Line = &Line{} eanimate.UpdateF = func(u *bytes.Buffer) { u.Reset() // Left to Right if Unicode { for i := 0; i < 16; i++ { if i == w.Index { u.WriteRune('\u25a0') } else { u.WriteByte(' ') } } /* var buffer []rune = []rune(strings.Repeat(" ", 16)) buffer[w.Index] = '\u25a0' u.WriteString(string(buffer)) */ } else { for i := 0; i < 16; i++ { if i == w.Index { u.WriteByte('\xfe') } else { u.WriteByte(' ') } } /* var buffer []byte = bytes.Repeat([]byte(" "), 16) buffer[w.Index] = '\xfe' u.WriteString(string(buffer)) */ } } eanimate.Update() // Text.Write(eanimate.UpdateF()) w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, eanimate) w.RemainingPanel = Panel{Width: 16, BorderColor: color, Style: SINGLE} w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, NewLine(" GAME ")) w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, NewLine(" TIME REMAINING ")) var rhour *Line = &Line{} rhour.UpdateF = func(u *bytes.Buffer) { var hours int = int(w.RemainingD.Hours()) u.Reset() /* u.WriteString(" ") u.WriteByte(byte(int('0') + (hours%100)/10)) u.WriteByte(byte(int('0') + (hours%100)%10)) u.WriteString(" HRS ") */ fmt.Fprintf(u, " %02d HRS ", hours%100) } rhour.Update() // Text.Write(rhour.UpdateF()) w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, rhour) var rminsec *Line = &Line{} rminsec.UpdateF = func(u *bytes.Buffer) { var mins int = int(w.RemainingD.Minutes()) % 60 var secs int = int(w.RemainingD.Seconds()) % 60 u.Reset() fmt.Fprintf(u, " %02d MIN %02d SEC ", mins, secs) } rminsec.Update() // Text.Write(rminsec.UpdateF()) w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, rminsec) var ranimate *Line = &Line{} ranimate.UpdateF = func(u *bytes.Buffer) { u.Reset() // Left to Right if Unicode { // Changing to for loop from buffers did 0. for i := 0; i < 16; i++ { if i == 15-w.Index { u.WriteRune('\u25a0') } else { u.WriteByte(' ') } } /* var buffer []rune = []rune(strings.Repeat(" ", 16)) buffer[15-w.Index] = '\u25a0' u.WriteString(string(buffer)) */ } else { for i := 0; i < 16; i++ { if i == 15-w.Index { u.WriteByte('\xfe') } else { u.WriteByte(' ') } } // var buffer []byte = bytes.Repeat([]byte{' '}, 16) // No change here. /* var buffer [16]byte = [16]byte{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '} */ // bytes.Repeat([]byte(" "), 16) /* buffer[15-w.Index] = '\xfe' u.Write(buffer[:]) // u.WriteString(string(buffer)) */ } } ranimate.Update() // Text.Write(ranimate.UpdateF()) w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, ranimate) } func (w *WOPR) Inc() { w.Index++ if w.Index == 16 { w.Index = 0 w.ElapsedPanel.Update() w.RemainingPanel.Update() w.RemainingD = time.Duration(w.RemainingD.Seconds()-1) * time.Second w.ElapsedD = time.Duration(w.ElapsedD.Seconds()+1) * time.Second } } var woprBytesUsed int func (w *WOPR) Output() []byte { w.output.Reset() w.output.WriteString(SavePos) w.output.Write(w.ElapsedPanel.Output()) w.output.Write(w.RemainingPanel.Output()) w.output.WriteString(RestorePos) if DEBUG_WOPR { if w.output.Cap() > woprBytesUsed { woprBytesUsed = w.output.Cap() log.Printf("WOPR: now %d\n", woprBytesUsed) } } return w.output.Bytes() } func (w *WOPR) Animate(d *Door) { // til := time.Now().UnixNano() % int64(time.Second) // w.Index = int((til / int64(time.Microsecond)) / 62500) // either put the sync sleep in the go routine, or sleep and sync Index. // time.Sleep(time.Duration(int64(time.Second) - til)) // time.Now().UnixMilli()%1000) * time.Millisecond) // time.Second / 16 // w.Ticker = time.NewTicker(time.Microsecond * time.Duration(62500)) // w.Index = 0 // Convert time.Time to time.Duration // This gives us consistency when resuming. w.ElapsedD = time.Since(w.Elapsed) w.RemainingD = time.Until(w.Remaining) w.StopIt = make(chan bool) go func(d *Door) { // til := time.Now().UnixNano() % int64(time.Second) sec16 := int64(time.Second) / 16 // tilms := til % sec16 w.Index = 0 // int(til / sec16) // log.Printf("til: %d, sec: %d, w.Index: %d\n", til, sec16, w.Index) // either put the sync sleep in the go routine, or sleep and sync Index. // time.Sleep(time.Second - time.Duration(til)) // sec16 - (til % sec16))) //int64(time.Second) - til)) // time.Now().UnixMilli()%1000) * time.Millisecond) // time.Second / 16 w.Ticker = time.NewTicker(time.Duration(sec16)) // var output bytes.Buffer for { select { case <-w.StopIt: return case <-w.Ticker.C: w.ElapsedPanel.Update() w.RemainingPanel.Update() //var output []byte = w.Output() if !d.Writer.IsClosed() { // d.Write(output) d.Write(w.Output()) } else { w.Ticker.Stop() return } w.Inc() } } }(d) } func (w *WOPR) Stop() { w.Ticker.Stop() w.StopIt <- true }