feat: a ton of input handling and Go TUI logic

Main: Added main interface with big output box and small input box
Effects: Added some blinking lights effect for the cursor to prompt user
of where to enter stuff
This commit is contained in:
Zhongheng Liu 2024-10-23 21:26:34 +03:00
commit dc16793ad7
Signed by: steven
GPG key ID: DC8F48E7B4C40905
3 changed files with 135 additions and 22 deletions

View file

@ -1,14 +1,30 @@
package main package main
import ( import (
// "fmt"
"time" "time"
. "github.com/gbin/goncurses" . "github.com/gbin/goncurses"
"gitlab.com/stvnliu/ai_game/utils/helper"
. "gitlab.com/stvnliu/ai_game/utils/types" . "gitlab.com/stvnliu/ai_game/utils/types"
"gitlab.com/stvnliu/ai_game/utils/windows" "gitlab.com/stvnliu/ai_game/utils/windows"
"gitlab.com/stvnliu/ai_game/utils/helper"
) )
const (
STD_BLINK_INTERVAL = 450 * time.Millisecond
)
func IncrementalPrintMany(
w *Window,
y int,
x int,
texts []string,
duration time.Duration,
) {
for i := 0; i < len(texts); i++ {
helper.IncrementalPrint(w, texts[i], y+i, x, int(1*time.Second))
}
}
func NewGame(scr *Window) { func NewGame(scr *Window) {
//_, _ := scr.MaxYX() //_, _ := scr.MaxYX()
game_name := windows.InputPrompt(scr, " New game information ", "Game name: ", 20) game_name := windows.InputPrompt(scr, " New game information ", "Game name: ", 20)
@ -35,32 +51,75 @@ func NewGame(scr *Window) {
panic("Oh shit something happened that shouldn't") panic("Oh shit something happened that shouldn't")
} }
w.Box(0, 0) w.Box(0, 0)
input_window, input_window_error := NewWindow(6,mx-3,my-7,2) input_window, input_window_error := NewWindow(6, mx-3, my-7, 2)
if input_window_error != nil { if input_window_error != nil {
panic("Oh no") panic("Oh no")
} }
input_window.Box(1, 1) input_window.Box(1, 1)
input_window.MovePrint(1, 1, "> ") input_window.MovePrint(1, 1, "> ")
input_window.Move(1, 3) input_window.Move(1, 3)
w.Refresh() w.Refresh()
input_window.Refresh() input_window.Refresh()
texts := []string { texts := []string{
"Hello world!!", "Hello world!!",
"Welcome to R.U.L.M.A.R.C.", "Welcome to R.U.L.M.A.R.C.",
"This is an experimental game project that uses Large Language Models to power realistically rendered characters.", "This is an experimental game project that uses Large Language Models to power realistically rendered characters.",
"============ Copyright 2024 @ Zhongheng Liu & Zhiyong He =============", "============ Copyright 2024 @ Zhongheng Liu & Zhiyong He =============",
"Try it! Put in some characters in the input box below!", "Please wait while we boot some AI models for the first part of the game...",
"Please wait while we boot some Artificial Intelligencce models for the first part of the game...", }
} IncrementalPrintMany(w, 1, 1, texts, time.Duration(1*time.Second))
for i := 0; i < len(texts); i++ {
helper.IncrementalPrint(w, texts[i], 1+i, 1, 500) init_done := make(chan bool, 1)
}
go helper.BlinkCursorUntilDone(
w,
len(texts)+1,
1,
STD_BLINK_INTERVAL,
init_done,
)
// Simulating game init process
// time.Sleep(time.Duration(10 * time.Second))
init_done <- true // can trigger blinker process finish
texts2 := []string{
"Ok we are done with everything!",
"Now try putting something in the input box below!",
}
IncrementalPrintMany(w, len(texts)+1, 1, texts2, time.Duration(1*time.Second))
key := helper.BlinkCursorUntilInput(input_window, 1, 3, STD_BLINK_INTERVAL)
Cursor(0)
my_input := "You said: "
for {
Cursor(2)
_, cx := input_window.CursorYX()
if key != 0 {
// workaround for backspace key
if (key == KEY_BACKSPACE) || (key == 127) {
if cx-1 > 2 {
input_window.MoveDelChar(1, cx-1)
my_input = my_input[:len(my_input)-1]
}
} else if !((key == KEY_ENTER) || (key == KEY_RETURN)) {
input_window.MovePrint(1, cx, KeyString(key))
my_input += KeyString(key)
input_window.Move(1, cx+1)
} else {
break
}
}
key = input_window.GetChar()
}
helper.IncrementalPrint(w, my_input, 8, 1, int(time.Duration(1*time.Second)))
// User input processing
for { for {
ch := w.GetChar() ch := w.GetChar()
switch Key(ch) { switch Key(ch) {
case 'q': case 'q':
w.Erase() w.Erase()
w.Refresh() w.Refresh()
w.Delete()
return return
} }
w.Refresh() w.Refresh()

54
utils/helper/effects.go Normal file
View file

@ -0,0 +1,54 @@
package helper
import (
"fmt"
"time"
. "github.com/gbin/goncurses"
)
func BlinkCursorUntilInput(scr *Window, pos_y int, pos_x int, interval time.Duration) Key {
scr.Move(pos_y, pos_x)
var activation_key Key
for {
Cursor(2)
scr.Timeout(int((interval / 3).Milliseconds()))
activation_key = scr.GetChar()
if activation_key != 0 {
break
}
time.Sleep(interval / 3)
Cursor(0)
time.Sleep(interval / 3)
}
return activation_key
}
func BlinkCursorUntilDone(scr *Window, pos_y int, pos_x int, interval time.Duration, done <-chan bool) {
scr.Move(pos_y, pos_x)
for {
Cursor(2)
select {
case is_done, ok := <-done:
if ok && is_done {
return
} else {
fmt.Println("Channel closed?")
}
default:
time.Sleep(interval / 2)
Cursor(0)
time.Sleep(interval / 2)
}
}
}
func BlinkCursorWithTime(scr *Window, pos_y int, pos_x int, duration time.Duration, interval time.Duration) {
scr.Move(pos_y, pos_x)
n := duration / interval
for i := 0; i < int(n); i++ {
Cursor(2)
time.Sleep(interval / 2)
Cursor(0)
time.Sleep(interval / 2)
}
}

View file

@ -10,8 +10,8 @@ func IncrementalPrint(scr *Window, text string, from_y int, from_x int, interval
for i:=0; i < len(text); i++ { for i:=0; i < len(text); i++ {
ch := string([]rune(text)[i]) ch := string([]rune(text)[i])
_, mx := scr.MaxYX() _, mx := scr.MaxYX()
cy := i / mx + 2 + from_y cy := i / mx + from_y
cx := i % mx + 2 cx := i % mx + 1
scr.MovePrint(cy, cx, ch) scr.MovePrint(cy, cx, ch)
time.Sleep( time.Duration(1000 / len(text)) * time.Millisecond) time.Sleep( time.Duration(1000 / len(text)) * time.Millisecond)
scr.Refresh() scr.Refresh()