123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- package main
- import (
- "fmt"
- "log"
- "math/rand"
- "os"
- "path/filepath"
- "red-green/door"
- "strconv"
- "strings"
- "time"
- "unicode"
- "github.com/seehuhn/mt19937"
- )
- var Config map[string]string
- func press_a_key(d *door.Door) rune {
- green := door.ColorText("BOLD GREEN")
- blue := door.ColorText("BOLD BLUE")
- yellow := door.ColorText("BOLD YELLOW")
- any_caps := green
- any_lower := yellow
- any_color := func(text string) string {
- var output string
- var lastColor string
- for _, letter := range text {
- if unicode.IsUpper(letter) {
- if lastColor != any_caps {
- lastColor = any_caps
- output += any_caps
- }
- output += string(letter)
- } else {
- if lastColor != any_lower {
- lastColor = any_lower
- output += any_lower
- }
- output += string(letter)
- }
- }
- return output
- }
- text := "Press a key to continue..."
- work_text := []rune(text)
- d.Write(door.Reset + any_color(text))
- var r rune
- var ex door.Extended
- var err error
- var t int
- sleep_ms := 250
- ms_sleep := 0
- words := find_words(text)
- var word int = 0
- var wpos int = 0
- current_word := text[words[word][0]:words[word][1]]
- t = 0
- r, ex, err = d.WaitKey(time.Duration(sleep_ms) * time.Millisecond) // 0, int64(sleep_ms)*1000)
- for err == door.ErrTimeout && ex == door.NOP { // r == -1 {
- ms_sleep += sleep_ms
- if ms_sleep > 1000 {
- ms_sleep -= 1000
- t++
- if t >= int(door.Inactivity) {
- d.Write(strings.Repeat("\b", len(text)))
- d.Write(any_color(text) + door.CRNL)
- return -1
- }
- }
- wpos++
- if wpos == len(current_word) {
- word++
- wpos = 0
- work_text = []rune(text)
- if word == len(words) {
- word = 0
- if any_caps == green {
- any_caps = blue
- } else {
- any_caps = green
- }
- d.Write(strings.Repeat("\b", len(text)))
- d.Write(any_color(string(work_text)))
- current_word = text[words[word][0]:words[word][1]]
- goto READKEY
- }
- current_word = text[words[word][0]:words[word][1]]
- }
- {
- c := rune(current_word[wpos])
- for !unicode.IsLower(c) {
- wpos++
- if wpos == len(current_word) {
- wpos = 0
- word++
- work_text = []rune(text)
- if word == len(words) {
- word = 0
- }
- current_word = text[words[word][0]:words[word][1]]
- }
- c = rune(current_word[wpos])
- }
- // Ok, we found something that's lower case
- work_text[words[word][0]+wpos] = unicode.ToUpper(c)
- }
- d.Write(strings.Repeat("\b", len(text)))
- d.Write(any_color(string(work_text)))
- READKEY:
- r, ex, err = d.WaitKey(time.Duration(sleep_ms) * time.Millisecond) // 0, int64(sleep_ms)*1000)
- }
- d.Write(strings.Repeat("\b", len(text)))
- d.Write(any_color(text))
- return r
- }
- func display_information(d *door.Door) {
- d.Write(door.Clrscr)
- keyColor := door.ColorText("BRI GREEN")
- sepColor := door.ColorText("BRI YEL")
- valColor := door.ColorText("CYAN")
- nice_format := func(key string, value string) string {
- return fmt.Sprintf("%s%-20s %s: %s%s", keyColor, key, sepColor, valColor, value) + door.CRNL
- }
- d.Write(nice_format("BBS Software", d.Config.BBSID))
- d.Write(nice_format("Real Name", d.Config.Real_name))
- d.Write(nice_format("Handle", d.Config.Handle))
- d.Write(nice_format("User #", strconv.Itoa(d.Config.User_number)))
- d.Write(nice_format("Security Level", strconv.Itoa(d.Config.Security_level)))
- d.Write(nice_format("Node #", strconv.Itoa(d.Config.Node)))
- d.Write(nice_format("Unicode", strconv.FormatBool(door.Unicode)))
- d.Write(nice_format("CP437", strconv.FormatBool(door.CP437)))
- d.Write(nice_format("Screen Size", fmt.Sprintf("%d X %d", door.Width, door.Height)))
- }
- func panel_demo(d *door.Door) {
- width := 55
- fmtStr := "%-55s"
- p := door.Panel{X: 5, Y: 5, Width: width, Style: door.DOUBLE, BorderColor: door.ColorText("CYAN ON BLUE"), Title: "[ Panel Demo ]"}
- lineColor := door.ColorText("BRIGHT WHI ON BLUE")
- // Add lines to the panel
- for _, line := range []string{"The BBS Door Panel Demo", "(C) 2021 Red Green Software, https://red-green.com"} {
- if door.Unicode {
- line = strings.Replace(line, "(C)", "\u00a9", -1)
- }
- l := door.Line{Text: fmt.Sprintf(fmtStr, line), DefaultColor: lineColor}
- p.Lines = append(p.Lines, l)
- }
- p.Lines = append(p.Lines, p.Spacer())
- p.Lines = append(p.Lines, door.Line{Text: fmt.Sprintf(fmtStr, "Welcome to golang!"), DefaultColor: lineColor})
- d.Write(door.Clrscr)
- d.Write(p.Output() + door.CRNL)
- }
- /*
- door::renderFunction statusValue(door::ANSIColor status,
- door::ANSIColor value) {
- door::renderFunction rf = [status,
- value](const std::string &txt) -> door::Render {
- door::Render r(txt);
- size_t pos = txt.find(':');
- if (pos == std::string::npos) {
- for (char const &c : txt) {
- if (std::isdigit(c))
- r.append(value);
- else
- r.append(status);
- }
- } else {
- pos++;
- r.append(status, pos);
- r.append(value, txt.length() - pos);
- }
- return r;
- };
- return rf;
- }
- */
- func RenderStatusValue(status string, value string) func(string) string {
- renderF := func(text string) string {
- pos := strings.Index(text, ":")
- if pos != -1 {
- return status + text[:pos] + value + text[pos:]
- } else {
- var r door.Render = door.Render{Line: text}
- for _, letter := range text {
- if unicode.IsDigit(letter) {
- r.Append(value, 1)
- } else {
- r.Append(status, 1)
- }
- }
- return r.Result
- }
- }
- return renderF
- }
- // Display the SysOp settings in the Config/yaml file
- func DisplaySettings(d *door.Door) {
- d.Write(door.Clrscr + door.Reset)
- W := 35
- panel := door.Panel{Width: W,
- X: 5,
- Y: 5,
- Style: door.DOUBLE,
- BorderColor: door.ColorText("BOLD CYAN ON BLUE"),
- }
- l := door.Line{Text: fmt.Sprintf("%*s", W, "Game Settings - SysOp Configurable"),
- DefaultColor: door.ColorText("BOLD GREEN ON BLUE")}
- panel.Lines = append(panel.Lines, l)
- renderF := RenderStatusValue(door.ColorText("BOLD YELLOW ON BLUE"), door.ColorText("BOLD GREEN ON BLUE"))
- for key, value := range Config {
- if key[0] == '_' {
- continue
- }
- key = strings.Replace(key, "_", " ", -1)
- l = door.Line{Text: fmt.Sprintf("%20s : %*s", key, -(W - 23), value), RenderF: renderF}
- panel.Lines = append(panel.Lines, l)
- }
- d.Write(panel.Output() + door.Reset + door.CRNL)
- press_a_key(d)
- }
- func Configure(d *door.Door, db *DBData) {
- menu := ConfigMenu()
- // deckcolor := "DeckColor"
- // save_deckcolor := false
- var choice int
- for choice >= 0 {
- d.Write(door.Clrscr)
- choice = menu.Choose(d)
- if choice < 0 {
- break
- }
- option := menu.GetOption(choice)
- switch option {
- case 'D':
- // Deck color
- current_color := db.GetSetting("DeckColor", "All")
- colormenu := DeckColorMenu(current_color)
- d.Write(door.Clrscr)
- c := colormenu.Choose(d)
- if c > 0 {
- db.SetSetting("DeckColor", DeckColors[c-1])
- }
- d.Write(door.CRNL)
- press_a_key(d)
- case 'V':
- // View settings
- DisplaySettings(d)
- case 'Q':
- choice = -1
- }
- }
- }
- func Help() door.Panel {
- W := 60
- center_x := (door.Width - W) / 2
- center_y := (door.Height - 16) / 2
- help := door.Panel{X: center_x,
- Y: center_y,
- Width: W,
- Style: door.DOUBLE,
- BorderColor: door.ColorText("BOLD YELLOW ON BLUE")}
- help.Lines = append(help.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "Help"),
- DefaultColor: door.ColorText("BOLD CYAN ON BLUE")})
- help.Lines = append(help.Lines, help.Spacer())
- copyright := SPACEACE + " v" + SPACEACE_VERSION
- help.Lines = append(help.Lines,
- door.Line{Text: fmt.Sprintf("%*s", -W, copyright),
- DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
- copyright = SPACEACE_COPYRIGHT
- if door.Unicode {
- copyright = strings.Replace(copyright, "(C)", "\u00a9", -1)
- }
- help.Lines = append(help.Lines,
- door.Line{Text: fmt.Sprintf("%*s", -W, copyright),
- DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
- for _, text := range []string{"",
- "Use Left/Right arrow keys, or 4/6 keys to move marker.",
- "The marker wraps around the sides of the screen.", "",
- "Select card to play with Space or 5.",
- "A card can play if it is higher or lower in rank by 1.",
- "",
- "Enter draws another card.",
- "",
- "Example: A Jack could play either a Ten or a Queen.",
- "Example: A King could play either an Ace or a Queen.",
- "",
- "The more cards in your streak, the more points earn.",
- } {
- help.Lines = append(help.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, text)})
- }
- return help
- }
- func About() door.Panel {
- W := 60
- center_x := (door.Width - W) / 2
- center_y := (door.Height - 16) / 2
- about := door.Panel{X: center_x,
- Y: center_y,
- Width: W,
- Style: door.SINGLE_DOUBLE,
- BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
- }
- about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "About This Door"),
- DefaultColor: door.ColorText("BOLD CYAN ON BLUE")})
- about.Lines = append(about.Lines, about.Spacer())
- copyright := SPACEACE + " v" + SPACEACE_VERSION
- about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, copyright)})
- copyright = SPACEACE_COPYRIGHT
- if door.Unicode {
- copyright = strings.Replace(copyright, "(C)", "\u00a9", -1)
- }
- about.Lines = append(about.Lines,
- door.Line{Text: fmt.Sprintf("%*s", -W, copyright),
- DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
- for _, text := range []string{"",
- "This door was written by Bugz.",
- "",
- "It is written in Go, understands CP437 and unicode, adapts",
- "to screen sizes, uses door32.sys, supports TheDraw Fonts,",
- "and runs on Linux and Windows."} {
- about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, text)})
- }
- return about
- }
- func MakeScoresRender(dateColor string, datelen int, nickColor string, nicklen int, scoreColor string) func(string) string {
- renderF := func(text string) string {
- var r door.Render = door.Render{Line: text}
- r.Append(dateColor, datelen)
- r.Append(nickColor, nicklen)
- r.Append(scoreColor, len(text)-(datelen+nicklen))
- return r.Result
- }
- return renderF
- }
- func DisplayScores(Door *door.Door, db *DBData, config *map[string]string) {
- // Scores::score()
- // make_top_scores_panel()
- W := 38
- var top_scores_panel door.Panel = door.Panel{Width: W,
- Style: door.DOUBLE,
- BorderColor: door.ColorText("CYAN ON BLUE"),
- Title: "[ The TOP Monthly Scores: ]",
- }
- const nick_len int = 17 // Max Nick Length
- var date_monthly string
- var ok bool
- date_monthly, ok = (*config)["date_monthly"]
- if !ok {
- date_monthly = "January"
- }
- var longest_month int = len(FormatDate(1631280600, date_monthly))
- monthly := db.GetMonthlyScores(15)
- if len(monthly) == 0 {
- top_scores_panel.Lines = append(top_scores_panel.Lines,
- door.Line{Text: fmt.Sprintf("%*s", -W, "No, Not Yet!"),
- DefaultColor: door.ColorText("BRI YELLOW ON BLUE"),
- })
- } else {
- ScoresRender := MakeScoresRender(door.ColorText("BOLD WHITE ON BLUE"),
- longest_month,
- door.ColorText("CYAN ON BLUE"),
- nick_len,
- door.ColorText("BOLD CYAN ON BLUE"))
- YourScoresRender := MakeScoresRender(
- door.ColorText("BOLD WHITE ON BLUE"),
- longest_month,
- door.ColorText("BOLD GREEN ON BLUE"),
- nick_len,
- door.ColorText("BOLD YELLOW ON BLUE"))
- for idx := range monthly {
- // What?
- timeDate := ToTime(monthly[idx].Date).Unix()
- var result string = fmt.Sprintf("%*s", -longest_month,
- FormatDate(timeDate, date_monthly))
- result += " " + fmt.Sprintf("%*s", -(nick_len-1), monthly[idx].User)
- result += fmt.Sprintf(" %d", monthly[idx].Score)
- result += strings.Repeat(" ", W-len(result))
- if monthly[idx].User == Door.Config.Real_name || monthly[idx].User == Door.Config.Handle {
- top_scores_panel.Lines = append(top_scores_panel.Lines,
- door.Line{Text: result, RenderF: YourScoresRender})
- } else {
- top_scores_panel.Lines = append(top_scores_panel.Lines,
- door.Line{Text: result, RenderF: ScoresRender})
- }
- }
- }
- // end make_top_scores_panel
- // make_top_this_month_panel()
- W = 38
- var title string = fmt.Sprintf("[ The TOP Scores for %s: ]",
- FormatDate(time.Now().Unix(), date_monthly))
- var top_month_panel door.Panel = door.Panel{Width: W,
- Style: door.DOUBLE,
- BorderColor: door.ColorText(""),
- Title: fmt.Sprintf("%*s", -W, title),
- }
- scores := db.GetScores(15)
- if len(scores) == 0 {
- top_month_panel.Lines = append(top_month_panel.Lines,
- door.Line{Text: fmt.Sprintf("%*s", -W, "No, Not Yet!"),
- DefaultColor: door.ColorText("BRI YELLOW ON BLUE"),
- })
- } else {
- }
- // Scores.display_scores()
- }
- func main() {
- var message string
- // Get path to binary, and chdir to it.
- binaryPath, _ := os.Executable()
- binaryPath = filepath.Dir(binaryPath)
- _ = os.Chdir(binaryPath)
- fmt.Println("Starting space-ace")
- rng := rand.New(mt19937.New())
- rng.Seed(time.Now().UnixNano())
- d := door.Door{}
- d.Init("space-ace")
- defer d.Close()
- db := DBData{}
- db.Open("space-database.db")
- defer db.Close()
- // Use Real_name or Handle? Or read config?
- db.User = d.Config.Real_name
- const config_filename = "space-ace.yaml"
- var err error
- if FileExists(config_filename) {
- Config, err = LoadConfig(config_filename)
- if err != nil {
- log.Printf("LoadConfig( %s ): %s\n", config_filename, err)
- Config = make(map[string]string)
- }
- } else {
- Config = make(map[string]string)
- }
- var update_config bool = false
- // Guessing at this point as to what settings I actually want.
- config_defaults := map[string]string{"hands_per_day": "3",
- "date_format": "January 2",
- "date_score": "01/02/2006",
- "makeup_per_day": "5",
- "play_days_ahead": "2",
- "date_monthly": "January 2006"}
- // _seed
- _, ok := Config["_seed"]
- if !ok {
- // Initialize the seed
- Config["_seed"] = fmt.Sprintf("%d,%d,%d", rng.Int31(), rng.Int31(), rng.Int31())
- update_config = true
- }
- for key, value := range config_defaults {
- if SetConfigDefault(&Config, key, value) {
- update_config = true
- }
- }
- if update_config {
- SaveConfig(config_filename, Config)
- }
- starfield := StarField{}
- starfield.Regenerate(rng)
- starfield.Display(&d)
- // d.Write(door.Goto(1, 1))
- cx := (door.Width - 72) / 2
- if cx < 0 {
- cx = 1
- }
- for idx, line := range ANSISpace() {
- d.Write(door.Goto(cx, idx+3) + line)
- }
- d.Write(door.Reset + door.CRNL)
- // Get the last call value (if they have called before)
- last_call, _ := strconv.ParseInt(db.GetSetting("LastCall", "0"), 10, 64)
- now := time.Now()
- db.SetSetting("LastCall", fmt.Sprintf("%d", now.Unix()))
- // Check for maint run -- get FirstOfMonthDate, and see if
- // records are older then it is. (If so, yes -- run maint)!
- db.ExpireScores(0)
- if last_call != 0 {
- d.Write("Welcome Back!" + door.CRNL)
- delta := now.Sub(time.Unix(last_call, 0))
- hours := delta.Hours()
- if hours > 24 {
- d.Write(fmt.Sprintf("It's been %0.1f days since you last played."+door.CRNL, hours/24))
- } else {
- if hours > 1 {
- d.Write(fmt.Sprintf("It's been %0.1f hours since you last played."+door.CRNL, hours))
- } else {
- minutes := delta.Minutes()
- d.Write(fmt.Sprintf("It's been %0.1f minutes since you last played."+door.CRNL, minutes))
- }
- }
- }
- left := d.TimeLeft()
- message = fmt.Sprintf("You have %0.2f minutes / %0.2f seconds remaining..."+door.CRNL, left.Minutes(), left.Seconds())
- d.Write(message)
- press_a_key(&d)
- mainmenu := MainMenu()
- var choice int
- for choice >= 0 {
- d.Write(door.Clrscr)
- starfield.Display(&d)
- choice = mainmenu.Choose(&d)
- if choice < 0 {
- break
- }
- option := mainmenu.GetOption(choice)
- // fmt.Printf("Choice: %d, Option: %c\n", choice, option)
- switch option {
- case 'P':
- // Play cards
- pc := PlayCards{Door: &d,
- DB: &db,
- Config: &Config,
- RNG: rng,
- }
- pc.Init()
- pc.Play()
- case 'S':
- // View Scores
- DisplayScores(&d, &db, &Config)
- press_a_key(&d)
- case 'C':
- // Configure
- Configure(&d, &db)
- case 'H':
- // Help
- h := Help()
- d.Write(door.Clrscr + h.Output() + door.Reset + door.CRNL)
- press_a_key(&d)
- case 'A':
- // About
- a := About()
- d.Write(door.Clrscr + a.Output() + door.Reset + door.CRNL)
- press_a_key(&d)
- case 'Q':
- choice = -1
- }
- }
- d.Write(door.Reset + door.CRNL)
- message = fmt.Sprintf("Returning to %s ..."+door.CRNL, d.Config.BBSID)
- d.Write(message)
- // d.WaitKey(1, 0)
- left = d.TimeLeft()
- message = fmt.Sprintf("You had %0.2f minutes / %0.2f seconds remaining!"+door.CRNL, left.Minutes(), left.Seconds())
- d.Write(message)
- left = d.TimeUsed()
- d.Write(fmt.Sprintf("You used %0.2f seconds / %0.2f minutes."+door.CRNL, left.Seconds(), left.Minutes()))
- fmt.Println("Exiting space-ace")
- }
|