|
@@ -1,6 +1,7 @@
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
+ "fmt"
|
|
|
"math/rand"
|
|
|
"red-green/door"
|
|
|
"strconv"
|
|
@@ -8,22 +9,112 @@ import (
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
+type Pos struct {
|
|
|
+ X int
|
|
|
+ Y int
|
|
|
+ Level int
|
|
|
+}
|
|
|
+
|
|
|
+func CardPos(pos int) Pos {
|
|
|
+ var result Pos
|
|
|
+ const space = 3
|
|
|
+ const height = 3
|
|
|
+
|
|
|
+ if pos == 28 {
|
|
|
+ result = CardPos(23)
|
|
|
+ result.Y += height + 1
|
|
|
+ result.Level--
|
|
|
+ return result
|
|
|
+ } else {
|
|
|
+ if pos == 29 {
|
|
|
+ result = CardPos(22)
|
|
|
+ result.Y += height + 1
|
|
|
+ result.Level--
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const CARD_WIDTH = 5
|
|
|
+ var HALF_WIDTH int = 3
|
|
|
+ HALF_WIDTH += space / 2
|
|
|
+
|
|
|
+ const between = CARD_WIDTH + space
|
|
|
+
|
|
|
+ if pos < 3 {
|
|
|
+ result.Level = 1
|
|
|
+ result.Y = (result.Level-1)*(height-1) + 1
|
|
|
+ result.X = pos*(between*3) + between + HALF_WIDTH + space
|
|
|
+ return result
|
|
|
+ } else {
|
|
|
+ pos -= 3
|
|
|
+ }
|
|
|
+
|
|
|
+ if pos < 6 {
|
|
|
+ result.Level = 2
|
|
|
+ result.Y = (result.Level-1)*(height-1) + 1
|
|
|
+ group := pos / 2
|
|
|
+ result.X = pos*between + (group * between) + CARD_WIDTH + space*2
|
|
|
+ return result
|
|
|
+ } else {
|
|
|
+ pos -= 6
|
|
|
+ }
|
|
|
+
|
|
|
+ if pos < 9 {
|
|
|
+ result.Level = 3
|
|
|
+ result.Y = (result.Level-1)*(height-1) + 1
|
|
|
+ result.X = pos*between + HALF_WIDTH + space
|
|
|
+ return result
|
|
|
+ } else {
|
|
|
+ pos -= 9
|
|
|
+ }
|
|
|
+
|
|
|
+ if pos < 10 {
|
|
|
+ result.Level = 4
|
|
|
+ result.Y = (result.Level-1)*(height-1) + 1
|
|
|
+ result.X = pos*between + space
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ // failure
|
|
|
+ result.Level = -1
|
|
|
+ result.X = -1
|
|
|
+ result.Y = -1
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+// 0-29
|
|
|
+var CardPosition []Pos
|
|
|
+
|
|
|
+func init() {
|
|
|
+ CardPosition = make([]Pos, 30)
|
|
|
+ for x := 0; x < 30; x++ {
|
|
|
+ CardPosition[x] = CardPos(x)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
type PlayCards struct {
|
|
|
- Door *door.Door
|
|
|
- DB *DBData
|
|
|
- Config *map[string]string
|
|
|
- RNG *rand.Rand
|
|
|
- Seeds []int32
|
|
|
- Total_hands int
|
|
|
- Play_card int
|
|
|
- Current_streak int
|
|
|
- Best_streak int
|
|
|
- Select_card int
|
|
|
- Score int
|
|
|
- Play_day time.Time
|
|
|
- Play_day_t int64
|
|
|
- Days_played int
|
|
|
- Hand int
|
|
|
+ Door *door.Door
|
|
|
+ DB *DBData
|
|
|
+ Config *map[string]string
|
|
|
+ RNG *rand.Rand
|
|
|
+ Seeds []int32
|
|
|
+ Total_hands int
|
|
|
+ Play_card int
|
|
|
+ Current_streak int
|
|
|
+ Best_streak int
|
|
|
+ Select_card int
|
|
|
+ Score int
|
|
|
+ Play_day time.Time
|
|
|
+ Play_day_t int64
|
|
|
+ Days_played int
|
|
|
+ Hand int
|
|
|
+ Day_status [42]int
|
|
|
+ SpaceAceTriPeaks door.Panel
|
|
|
+ ScorePanel door.Panel
|
|
|
+ StreakPanel door.Panel
|
|
|
+ LeftPanel door.Panel
|
|
|
+ CmdPanel door.Panel
|
|
|
+ NextQuitPanel door.Panel
|
|
|
+ Calendar door.Panel
|
|
|
}
|
|
|
|
|
|
// Adjust date to 2:00 AM
|
|
@@ -35,8 +126,50 @@ func NormalizeDate(date *time.Time) {
|
|
|
*date = date.Add(-offset)
|
|
|
}
|
|
|
|
|
|
-func (pc *PlayCards) Init() {
|
|
|
- // init_values()
|
|
|
+func cmdLineRender(bracket string, inner string, outer string) func(string) string {
|
|
|
+ cmdRender := func(input string) string {
|
|
|
+ var result string
|
|
|
+ inOuter := true
|
|
|
+ var lastColor string
|
|
|
+
|
|
|
+ for _, c := range input {
|
|
|
+ if c == '[' {
|
|
|
+ inOuter = false
|
|
|
+ if lastColor != bracket {
|
|
|
+ result += bracket
|
|
|
+ lastColor = bracket
|
|
|
+ }
|
|
|
+ result += string(c)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if c == ']' {
|
|
|
+ inOuter = true
|
|
|
+ if lastColor != bracket {
|
|
|
+ result += bracket
|
|
|
+ lastColor = bracket
|
|
|
+ }
|
|
|
+ result += string(c)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if inOuter {
|
|
|
+ if lastColor != outer {
|
|
|
+ result += outer
|
|
|
+ lastColor = outer
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if lastColor != inner {
|
|
|
+ result += inner
|
|
|
+ lastColor = inner
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result += string(c)
|
|
|
+ }
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ return cmdRender
|
|
|
+}
|
|
|
+
|
|
|
+func (pc *PlayCards) InitValues() {
|
|
|
hands := pc.DB.GetSetting("hands_per_day", "3")
|
|
|
pc.Total_hands, _ = strconv.Atoi(hands)
|
|
|
pc.Play_card = 28
|
|
@@ -47,6 +180,12 @@ func (pc *PlayCards) Init() {
|
|
|
pc.Select_card = 23
|
|
|
pc.Score = 0
|
|
|
|
|
|
+}
|
|
|
+
|
|
|
+func (pc *PlayCards) Init() {
|
|
|
+ // init_values()
|
|
|
+ pc.InitValues()
|
|
|
+
|
|
|
// PlayCards::PlayCards()
|
|
|
pc.Play_day = time.Now()
|
|
|
NormalizeDate(&pc.Play_day)
|
|
@@ -76,6 +215,329 @@ func (pc *PlayCards) Init() {
|
|
|
pc.Hand = 0
|
|
|
pc.Total_hands = 0
|
|
|
|
|
|
- // Panels ?
|
|
|
+ // spaceAceTriPeaks = make_tripeaks();
|
|
|
+ {
|
|
|
+ tripeakstext := " " + SPACEACE + " - Tri-Peaks Solitaire v" + SPACEACE_VERSION + " "
|
|
|
+ pc.SpaceAceTriPeaks = door.Panel{Width: len(tripeakstext),
|
|
|
+ Style: door.SINGLE,
|
|
|
+ BorderColor: door.ColorText("CYAN ON BLACK"),
|
|
|
+ }
|
|
|
+ pc.SpaceAceTriPeaks.Lines = append(pc.SpaceAceTriPeaks.Lines,
|
|
|
+ door.Line{Text: tripeakstext})
|
|
|
+ }
|
|
|
+
|
|
|
+ svRender := RenderStatusValue(door.ColorText("BOLD WHITE ON BLUE"),
|
|
|
+ door.ColorText("BOLD YELLOW ON BLUE"))
|
|
|
+
|
|
|
+ // score_panel = make_score_panel();
|
|
|
+ {
|
|
|
+ W := 25
|
|
|
+ pc.ScorePanel = door.Panel{Width: W}
|
|
|
+ text := "Name: " + pc.Door.Config.Real_name
|
|
|
+ pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
|
|
|
+ door.Line{Text: fmt.Sprintf("%*s", -W, text), RenderF: svRender})
|
|
|
+
|
|
|
+ scoreUpdate := func() string {
|
|
|
+ txt := fmt.Sprintf("Score: %d", pc.Score)
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
|
|
|
+ door.Line{Text: scoreUpdate(),
|
|
|
+ UpdateF: scoreUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+
|
|
|
+ timeUpdate := func() string {
|
|
|
+ left := pc.Door.TimeLeft()
|
|
|
+ used := pc.Door.TimeUsed()
|
|
|
+ txt := fmt.Sprintf("Time used: %3d / %3d", used.Minutes(), left.Minutes())
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
|
|
|
+ door.Line{Text: timeUpdate(),
|
|
|
+ UpdateF: timeUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+
|
|
|
+ handUpdate := func() string {
|
|
|
+ txt := fmt.Sprintf("Playing Hand %d of %d", pc.Hand, pc.Total_hands)
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
|
|
|
+ door.Line{Text: handUpdate(),
|
|
|
+ UpdateF: handUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // streak_panel = make_streak_panel();
|
|
|
+ {
|
|
|
+ W := 20
|
|
|
+ pc.StreakPanel = door.Panel{Width: W}
|
|
|
+
|
|
|
+ dateUpdate := func() string {
|
|
|
+ format, ok := (*pc.Config)["date_format"]
|
|
|
+ if !ok {
|
|
|
+ format = "January 2"
|
|
|
+ }
|
|
|
+ txt := fmt.Sprintf("Playing: %s", pc.Play_day.Format(format))
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
|
|
|
+ door.Line{Text: dateUpdate(),
|
|
|
+ UpdateF: dateUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+ currentUpdate := func() string {
|
|
|
+ txt := fmt.Sprintf("Current Streak: %d", pc.Current_streak)
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
|
|
|
+ door.Line{Text: currentUpdate(),
|
|
|
+ UpdateF: currentUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+ longestUpdate := func() string {
|
|
|
+ txt := fmt.Sprintf("Longest Streak: %d", pc.Best_streak)
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
|
|
|
+ door.Line{Text: longestUpdate(),
|
|
|
+ UpdateF: longestUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // left_panel = make_left_panel();
|
|
|
+ {
|
|
|
+ W := 13
|
|
|
+ pc.LeftPanel = door.Panel{Width: W}
|
|
|
+ leftUpdate := func() string {
|
|
|
+ txt := fmt.Sprintf("Cards left:%d", 51-pc.Play_card)
|
|
|
+ txt += strings.Repeat(" ", W-len(txt))
|
|
|
+ return txt
|
|
|
+ }
|
|
|
+ pc.LeftPanel.Lines = append(pc.LeftPanel.Lines,
|
|
|
+ door.Line{Text: leftUpdate(),
|
|
|
+ UpdateF: leftUpdate,
|
|
|
+ RenderF: svRender,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // cmd_panel = make_command_panel();
|
|
|
+ {
|
|
|
+ W := 76
|
|
|
+ pc.CmdPanel = door.Panel{Width: W}
|
|
|
+ var commands string
|
|
|
+
|
|
|
+ if door.Unicode {
|
|
|
+ commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] Draw [Q]uit [R]edraw [H]elp"
|
|
|
+ commands += strings.Repeat(" ", W-len([]rune(commands)))
|
|
|
+ } else {
|
|
|
+ commands = "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit [R]edraw [H]elp"
|
|
|
+ commands += strings.Repeat(" ", W-len(commands))
|
|
|
+ }
|
|
|
+
|
|
|
+ cmdRender := cmdLineRender(door.ColorText("BOLD YELLOW ON BLUE"),
|
|
|
+ door.ColorText("BOLD CYAN ON BLUE"),
|
|
|
+ door.ColorText("BOLD GREEN ON BLUE"))
|
|
|
+ pc.CmdPanel.Lines = append(pc.CmdPanel.Lines,
|
|
|
+ door.Line{Text: commands,
|
|
|
+ RenderF: cmdRender})
|
|
|
+ }
|
|
|
+
|
|
|
+ // next_quit_panel = make_next_panel();
|
|
|
+ {
|
|
|
+ W := 50
|
|
|
+ pc.NextQuitPanel = door.Panel{Width: W,
|
|
|
+ Style: door.DOUBLE,
|
|
|
+ BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
|
|
|
+ }
|
|
|
+ nextUpdate := func() string {
|
|
|
+ var text string
|
|
|
+ if pc.Select_card != -1 {
|
|
|
+ text = "[C]ontinue this hand"
|
|
|
+ }
|
|
|
+ if pc.Hand < pc.Total_hands {
|
|
|
+ text += " [N]ext Hand [Q]uit"
|
|
|
+ } else {
|
|
|
+ text += " [D]one [Q]uit"
|
|
|
+ }
|
|
|
+ text = " " + text + " "
|
|
|
+ text += strings.Repeat(" ", W-len(text))
|
|
|
+ return text
|
|
|
+ }
|
|
|
+ pc.NextQuitPanel.Lines = append(pc.NextQuitPanel.Lines,
|
|
|
+ door.Line{Text: nextUpdate(),
|
|
|
+ UpdateF: nextUpdate,
|
|
|
+ RenderF: cmdLineRender(door.ColorText("BOLD YEL ON BLU"),
|
|
|
+ door.ColorText("BOLD CYAN ON BLU"),
|
|
|
+ door.ColorText("BOLD GREE ON BLUE"))})
|
|
|
+ }
|
|
|
+
|
|
|
+ // calendar = make_calendar();
|
|
|
+ {
|
|
|
+ W := 41
|
|
|
+ pc.Calendar = door.Panel{Width: W,
|
|
|
+ Style: door.DOUBLE,
|
|
|
+ BorderColor: door.ColorText("CYAN ON BLACK")}
|
|
|
+
|
|
|
+ calendarRender := func(text string) string {
|
|
|
+ // var result string
|
|
|
+ // var lastColor string
|
|
|
+ result := door.Render{Line: text}
|
|
|
+ digits := door.ColorText("CYAN ON BLACK")
|
|
|
+ digits_play := door.ColorText("BOLD CYAN ON BLACK")
|
|
|
+ spaces := door.ColorText("WHITE ON BLACK")
|
|
|
+ open := door.ColorText("BOLD GREEN ON BLACK")
|
|
|
+ hands := door.ColorText("BOLD YELLOW ON BLACK")
|
|
|
+ full := door.ColorText("RED ON BLACK")
|
|
|
+ nny := door.ColorText("BOLD BLACK ON BLACK")
|
|
|
+
|
|
|
+ for days := 0; days < 7; days++ {
|
|
|
+ var dayText string
|
|
|
+
|
|
|
+ if days == 0 {
|
|
|
+ result.Append(spaces, 1)
|
|
|
+ }
|
|
|
+ dayText = text[1+days*6 : (1+days*6)+3]
|
|
|
+ if dayText[1] == ' ' {
|
|
|
+ result.Append(spaces, 3)
|
|
|
+ } else {
|
|
|
+ // Something is here
|
|
|
+ cday := dayText[2]
|
|
|
+ if cday == 'o' || cday == 'h' {
|
|
|
+ result.Append(digits_play, 2)
|
|
|
+ } else {
|
|
|
+ result.Append(digits, 2)
|
|
|
+ }
|
|
|
+ switch cday {
|
|
|
+ case 'o':
|
|
|
+ result.Append(open, 1)
|
|
|
+ case 'h':
|
|
|
+ result.Append(hands, 1)
|
|
|
+ case 'x':
|
|
|
+ result.Append(full, 1)
|
|
|
+ case 'u':
|
|
|
+ result.Append(nny, 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if days == 6 {
|
|
|
+ result.Append(spaces, 1)
|
|
|
+ } else {
|
|
|
+ result.Append(spaces, 3)
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result.Result
|
|
|
+ }
|
|
|
+
|
|
|
+ for row := 0; row < 6; row++ {
|
|
|
+ calendarUpdate := func() string {
|
|
|
+ var text string
|
|
|
+
|
|
|
+ for d := 0; d < 7; d++ {
|
|
|
+ text += " "
|
|
|
+ v := pc.Day_status[(row*7)+d]
|
|
|
+ if v == 0 {
|
|
|
+ text += " "
|
|
|
+ } else {
|
|
|
+ text += fmt.Sprintf("%-2d")
|
|
|
+ status := pc.Day_status[v-1]
|
|
|
+ switch status {
|
|
|
+ case 0:
|
|
|
+ text += "o"
|
|
|
+ case 1:
|
|
|
+ text += "h"
|
|
|
+ case 2:
|
|
|
+ text += "x"
|
|
|
+ case 3:
|
|
|
+ text += "u"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if d == 6 {
|
|
|
+ text += " "
|
|
|
+ } else {
|
|
|
+ text += " "
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return text
|
|
|
+ }
|
|
|
+
|
|
|
+ pc.Calendar.Lines = append(pc.Calendar.Lines,
|
|
|
+ door.Line{Text: calendarUpdate(),
|
|
|
+ UpdateF: calendarUpdate,
|
|
|
+ RenderF: calendarRender})
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func (pc *PlayCards) Play() {
|
|
|
+ // this defaults (for now) to playing today (if unplayed).
|
|
|
+ // Otherwise display calendar.
|
|
|
+ pc.Play_day = time.Now()
|
|
|
+ NormalizeDate(&pc.Play_day)
|
|
|
+
|
|
|
+ pc.Play_day_t = pc.Play_day.Unix()
|
|
|
+ // played := db.HandsPlayedOnDay(pc.Play_day_t)
|
|
|
+ played := 0
|
|
|
+
|
|
|
+ var r int
|
|
|
+
|
|
|
+ if played == 0 {
|
|
|
+ pc.Door.Write("Let's play today..." + door.CRNL)
|
|
|
+ time.Sleep(time.Second)
|
|
|
+ pc.Hand = 1
|
|
|
+ r = pc.PlayCards()
|
|
|
+ if r != 'D' {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if played < pc.Total_hands {
|
|
|
+ // let's finish today...
|
|
|
+ pc.Hand = played + 1
|
|
|
+ r = pc.PlayCards()
|
|
|
+ if r != 'D' {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ CALENDAR_UPDATE:
|
|
|
+ pc.UpdateCalendarDays()
|
|
|
+ pc.Calendar.Update()
|
|
|
+
|
|
|
+ pc.Door.Write(pc.Calendar.Output())
|
|
|
+ */
|
|
|
+}
|
|
|
+
|
|
|
+func (pc *PlayCards) PlayCards() int {
|
|
|
+ pc.InitValues()
|
|
|
+
|
|
|
+ var game_width int
|
|
|
+ var game_height int = 20
|
|
|
+ pos := CardPos(27)
|
|
|
+ game_width = pos.X + 5
|
|
|
+ MX := door.Width
|
|
|
+ MY := door.Height
|
|
|
+
|
|
|
+ off_x := (MX - game_width) / 2
|
|
|
+ off_y := (MY - game_height) / 2
|
|
|
+
|
|
|
+ pc.SpaceAceTriPeaks.X = (MX - pc.SpaceAceTriPeaks.Width) / 2
|
|
|
+ pc.SpaceAceTriPeaks.Y = off_y
|
|
|
+ off_y += 3
|
|
|
+
|
|
|
+ currentDefault := pc.DB.GetSetting("DeckColor", "ALL")
|
|
|
|
|
|
+ return 0
|
|
|
}
|