package main import ( "fmt" "math/rand" "red-green/door" "strconv" "strings" "time" ) func StringToANSIColor(colorCode string) string { colors := []string{ "BLUE", "BROWN", "RED", "CYAN", "GREEN", "MAGENTA", "WHITE", } code := strings.ToUpper(colorCode) for _, c := range colors { if c == code { return door.ColorText(code) } } if code == "ALL" { rand.Seed(time.Now().UnixNano()) pos := rand.Intn(len(colors)) return door.ColorText(colors[pos]) } return door.ColorText("WHITE") } 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 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 func NormalizeDate(date *time.Time) { offset := time.Duration(date.Second())*time.Second + time.Duration(date.Minute())*time.Minute + time.Duration(date.Hour()-2)*time.Hour + time.Duration(date.Nanosecond())*time.Nanosecond *date = date.Add(-offset) } 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 pc.Current_streak = 0 pc.Best_streak = 0 best := pc.DB.GetSetting("best_streak", "0") pc.Best_streak, _ = strconv.Atoi(best) 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) pc.Play_day_t = pc.Play_day.Unix() var last_played int64 last := pc.DB.GetSetting("last_played", "0") last_played, _ = strconv.ParseInt(last, 10, 64) days := pc.DB.GetSetting("days_played", "0") pc.Days_played, _ = strconv.Atoi(days) if last_played != pc.Play_day_t { pc.DB.SetSetting("last_played", strconv.FormatInt(pc.Play_day_t, 10)) pc.DB.SetSetting("days_played", "0") pc.Days_played = 0 } pc.Seeds = make([]int32, 0) // config _seed parts := strings.Split((*pc.Config)["_seed"], ",") for _, seed := range parts { i, _ := strconv.ParseInt(seed, 10, 32) pc.Seeds = append(pc.Seeds, int32(i)) } pc.Hand = 0 pc.Total_hands = 0 // 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 // We can now position things properly centered pc.SpaceAceTriPeaks.X = (MX - pc.SpaceAceTriPeaks.Width) / 2 pc.SpaceAceTriPeaks.Y = off_y off_y += 3 currentDefault := pc.DB.GetSetting("DeckColor", "ALL") return 0 }