package main import ( "crypto/sha1" "encoding/binary" "fmt" "log" "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 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 Hand_streak int Month_streak int Select_card int Score int Play_day YMD Days_played int Hand int SpaceAceTriPeaks door.Panel ScorePanel door.Panel StreakPanel door.Panel LeftPanel door.Panel CmdPanel door.Panel NextQuitPanel door.Panel DeckPanel Deck Deck []DeckType // [51]DeckType t State []DeckType // [51]DeckType Off_X int Off_Y int Calendar door.Screen Calendar_panel_days [42]int // Where is each day positioned on the Calendar? 42 = 7 day * 6 lines Calendar_day_status [31]int } // Calendar_day [31]DBDate // deprecate // Possibly change Deck to [51]DeckType. This game always only has 1 deck. // Possibly change State [51]DeckType to [51]int8. // There are few states to track. func cmdLineRender(bracket string, inner string, outer string) func(string) string { return func(input string) string { var r door.Render = door.Render{Line: input} inOuter := true for _, c := range input { if c == '[' { inOuter = false r.Append(bracket, 1) continue } if c == ']' { inOuter = true r.Append(bracket, 1) continue } if inOuter { r.Append(outer, 1) } else { r.Append(inner, 1) } } return r.Result } } 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.Month_streak = 0 // Init the best streak for this month // TODO: Pull from DB. pc.Hand_streak = 0 // Best for this hand pc.Select_card = 23 pc.Score = 0 } func (pc *PlayCards) Init() { // init_values() pc.InitValues() // PlayCards::PlayCards() pc.Play_day = NewYMDFromTime(time.Now()) // NormalizeDate(&pc.Play_day) //pc.Play_day_t = pc.Play_day.Unix() pc.DeckPanel.Init() var last_played YMD last := pc.DB.GetSetting("last_played", "0") temp, _ := strconv.ParseInt(last, 10, 64) last_played = YMD(temp) days := pc.DB.GetSetting("days_played", "0") pc.Days_played, _ = strconv.Atoi(days) if last_played != pc.Play_day { pc.DB.SetSetting("last_played", strconv.FormatInt(int64(pc.Play_day), 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 // 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 { var left int = int(pc.Door.TimeLeft().Minutes()) var used int = int(pc.Door.TimeUsed().Minutes()) txt := fmt.Sprintf("Time used: %3d / %3d", used, left) 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", ToTime(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, }) handUpdate := func() string { txt := fmt.Sprintf("Hand Streak : %d", pc.Hand_streak) txt += strings.Repeat(" ", W-len(txt)) return txt } pc.StreakPanel.Lines = append(pc.StreakPanel.Lines, door.Line{Text: handUpdate(), UpdateF: handUpdate, RenderF: svRender, }) longestUpdate := func() string { txt := fmt.Sprintf("Longest Streak: %d", pc.Best_streak) txt += strings.Repeat(" ", W-len(txt)) return txt } // TODO: // Add: This Month and This Hand values // pc.Month_streak, pc.Hand_streak 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(); { pc.Calendar = door.Screen{} var month YMD = NewYMDFromTime(time.Now()) month.First() var today_day int = month.Day() // FirstOfMonthDate(&month) var month_end YMD = month month_end.Last() // clear out /* for x := range pc.Calendar_day { pc.Calendar_day[x] = 0 } // pc.Calendar_day_t = make([]int64, 31) pc.Calendar_day[0] = month // .Unix() */ var First_Weekday int = int(ToTime(month).Weekday()) var month_last_day = month_end.Day() /* var calendarDay DBDate = month for x := 1; x <= month_last_day; x++ { calendarDay.SetDay(x) // = DBDate((int(calendarDay) % 100) + x) pc.Calendar_day[x-1] = calendarDay } */ // var month_end time.Time = month /* for { month_end = month_end.AddDate(0, 0, 1) NormalizeDate(&month_end) if month_end.Day() == 1 { break } else { pc.Calendar_day_t[month_end.Day()-1] = month_end.Unix() month_last_day = month_end.Day() } } */ // clear out for x := range pc.Calendar_panel_days { pc.Calendar_panel_days[x] = 0 } // pc.Calendar_panel_days = make([]int, 6*7) var row int = 0 for x := 0; x < month_last_day; x++ { var dow int = (x + First_Weekday) % 7 if x != 0 && dow == 0 { row++ } pc.Calendar_panel_days[row*7+dow] = x + 1 } // clear out for x := range pc.Calendar_day_status { pc.Calendar_day_status[x] = 0 } // pc.Calendar_day_status = make([]int, 31) last_played := pc.DB.WhenPlayed() for played, hands := range last_played { if played >= month { // Ok, it is within the range var played_day int = played.Day() if hands < pc.Total_hands { pc.Calendar_day_status[played_day-1] = 1 } else { pc.Calendar_day_status[played_day-1] = 2 } } } // Get play_days_ahead from config var play_days_ahead int = 0 if playdays, has := (*pc.Config)["play_days_ahead"]; has { play_days_ahead, _ = strconv.Atoi(playdays) } // Mark all days ahead as NNY. for d := 0; d < 31; d++ { if today_day+play_days_ahead-1 < d { pc.Calendar_day_status[d] = 3 } } // Get current month as string var current string = strings.ToUpper(CurrentMonth(ToTime(month))) if len(current) < 6 { current = " " + current + " " } // FUTURE: Replace this with TDF rendering of Month. // make_month var MonthPanel door.Panel = door.Panel{X: 3, Y: 3, Width: 3, Style: door.DOUBLE, BorderColor: door.ColorText("BOLD YELLOW ON BLACK"), } for _, c := range current { MonthPanel.Lines = append(MonthPanel.Lines, door.Line{Text: fmt.Sprintf(" %c ", c)}) } pc.Calendar.AddPanel(MonthPanel) // make_weekday var WeekdayPanel door.Panel = door.Panel{X: 8, Y: 3, Width: 41, Style: door.DOUBLE, BorderColor: door.ColorText("BOLD CYAN ON BLACK"), } WeekdayPanel.Lines = append(WeekdayPanel.Lines, door.Line{Text: " SUN MON TUE WED THU FRI SAT "}) pc.Calendar.AddPanel(WeekdayPanel) // make_calendar_panel var CalendarPanel door.Panel { W := 41 CalendarPanel = 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] == ' ' { if dayText[0] == ' ' { 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++ { // Do this, or it doesn't get captured correctly. _row := row // _pc := pc calendarUpdate := func() string { var text string for d := 0; d < 7; d++ { text += " " v := pc.Calendar_panel_days[(_row*7)+d] if v == 0 { text += " " } else { text += fmt.Sprintf("%-2d", v) status := pc.Calendar_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 } log.Printf("line %d: [%s]\n", row, calendarUpdate()) CalendarPanel.Lines = append(CalendarPanel.Lines, door.Line{Text: calendarUpdate(), UpdateF: calendarUpdate, RenderF: calendarRender}) } } CalendarPanel.X = 8 CalendarPanel.Y = 6 pc.Calendar.AddPanel(CalendarPanel) } // end make_calendar } func (pc *PlayCards) Play() rune { // this defaults (for now) to playing today (if unplayed). // Otherwise display calendar. pc.Play_day = NewYMDFromTime(time.Now()) // NormalizeDate(&pc.Play_day) // pc.Play_day_t = pc.Play_day.Unix() played := pc.DB.HandsPlayedOnDay(pc.Play_day) log.Printf("HandsPlayedOnDay(%d), %d", pc.Play_day, played) var r rune if played == 0 { pc.Door.Write(door.CRNL + door.Reset + "Let's play today..." + door.CRNL) time.Sleep(time.Second * time.Duration(2)) pc.Hand = 1 r = pc.PlayCards() if r != 'D' { return r } } else { if played < pc.Total_hands { // let's finish today... pc.Door.Write(door.CRNL + door.Reset + "Let's finish today..." + door.CRNL) time.Sleep(time.Second * time.Duration(2)) pc.Hand = played + 1 r = pc.PlayCards() if r != 'D' { return r } } } CALENDAR_UPDATE: // pc.UpdateCalendarDays() month_t := pc.Play_day // pc.Calendar_day[0] month_t.SetDay(1) last_played := pc.DB.WhenPlayed() for played, hands := range last_played { if played >= month_t { // Ok, it is within the range var played_day int = played.Day() log.Printf("update: month_unix %d, played %d, hands %d (total %d)\n", month_t, played_day, hands, pc.Total_hands) if hands < pc.Total_hands { pc.Calendar_day_status[played_day-1] = 1 } else { pc.Calendar_day_status[played_day-1] = 2 } } } // log.Printf("%#v\n", pc.Calendar) log.Println("Calendar.Update()") pc.Calendar.Update() log.Println("Calendar.Output()") pc.Door.Write(pc.Calendar.Output()) var has_playable_day bool = false for x := 0; x < 31; x++ { status := pc.Calendar_day_status[x] if status == 0 || status == 1 { has_playable_day = true break } } if !has_playable_day { pc.Door.Write(door.CRNL + "Sorry, there are no days available to play." + door.CRNL) r = press_a_key(pc.Door) if r < 0 { return r } else { return 'Q' } } log.Println("Choose Day") pc.Door.Write(door.CRNL + "Please choose a day : " + door.SavePos) AGAIN: log.Println("Input") var toplay string = pc.Door.Input(3) log.Printf("Input was: %s\n", toplay) pc.Door.Write(door.RestorePos) var number int var err error number, err = strconv.Atoi(toplay) if err != nil { number = 0 } if number == 0 { return ' ' } var status int if number <= 31 { status = pc.Calendar_day_status[number-1] if status == 0 { // play full day pc.Hand = 1 pc.Play_day.SetDay(number) // = pc.Calendar_day[number-1] // pc.Play_day = time.Unix(pc.Play_day_t, 0) r = pc.PlayCards() if r == 'D' { goto CALENDAR_UPDATE } return r } if status == 1 { // Play half day pc.Play_day.SetDay(number) // = pc.Calendar_day[number-1] // pc.Play_day = time.Unix(pc.Play_day_t, 0) played := pc.DB.HandsPlayedOnDay(pc.Play_day) if played < pc.Total_hands { pc.Hand = played + 1 r = pc.PlayCards() if r == 'D' { goto CALENDAR_UPDATE } return r } } goto AGAIN } return ' ' } func (pc *PlayCards) PlayCards() rune { pc.InitValues() var game_width int var game_height int = 20 // pos := &CardPos[27] game_width = CardPos[27].X + 5 MX := door.Width MY := door.Height pc.Off_X = (MX - game_width) / 2 pc.Off_Y = (MY - game_height) / 2 // _ = off_x // We can now position things properly centered pc.SpaceAceTriPeaks.X = (MX - pc.SpaceAceTriPeaks.Width) / 2 pc.SpaceAceTriPeaks.Y = pc.Off_Y pc.Off_Y += 3 currentDefault := pc.DB.GetSetting("DeckColor", "ALL") Next_Hand: deck_color := StringToANSIColor(currentDefault) pc.DeckPanel.SetBackColor(deck_color) pc.Play_card = 28 pc.Select_card = 23 pc.Score = 0 pc.Current_streak = 0 pc.Hand_streak = 0 // Don't touch Month_streak. // Use play day to seed RNG { // Secret Squirrel method of seeding the RNG seed_seq := make([]byte, 0) for _, seed := range pc.Seeds { ba := Int32toByteArray(seed) seed_seq = append(seed_seq, ba[:]...) // sha1.Sum(ba[:]) } pd := Int64toByteArray(int64(pc.Play_day)) seed_seq = append(seed_seq, pd[:]...) // We also need the hand # that we're playing. seed_seq = append(seed_seq, byte(pc.Hand)) result := sha1.Sum(seed_seq) var seed int64 = int64(binary.BigEndian.Uint64(result[0:8])) pc.RNG.Seed(seed) // I'm seeing changes in the seed_seq bytes, but the seed is the same number. // log.Printf("%#v\nSeed %d\nLen %d\nresult %#v\n", seed_seq, seed, len(seed_seq), result) pc.Deck = ShuffleCards(pc.RNG, 1) pc.State = MakeCardStates(1) } // Position the panels { off_yp := pc.Off_Y + 11 left_panel_x := CardPos[18].X right_panel_x := CardPos[15].X pc.ScorePanel.X = left_panel_x + pc.Off_X pc.ScorePanel.Y = off_yp pc.StreakPanel.X = right_panel_x + pc.Off_X pc.StreakPanel.Y = off_yp pc.CmdPanel.X = left_panel_x + pc.Off_X pc.CmdPanel.Y = off_yp + 5 next_off_x := (MX - pc.NextQuitPanel.Width) / 2 pc.NextQuitPanel.X = next_off_x pc.NextQuitPanel.Y = CardPos[10].Y + pc.Off_Y } var Dealing bool = true var r rune var ex door.Extended var err error pc.Redraw(Dealing) Dealing = false pc.LeftPanel.Update() pc.Door.Write(door.Reset) var c *door.Panel var in_game bool = true var save_streak bool = false var waiting int = 0 for in_game { var output string output = pc.ScorePanel.Update() if output != "" { pc.Door.Write(output) { // Redisplay Mark / Selected Card Pos := &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[1] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output()) } } if save_streak { save_streak = false pc.DB.SetSetting("best_streak", strconv.Itoa(pc.Best_streak)) } waiting = 0 for waiting < int(door.Inactivity) { r, ex, err = pc.Door.WaitKey(time.Second) if err == door.ErrTimeout { // TIMEOUT is expected here waiting++ output = pc.ScorePanel.Update() if output != "" { pc.Door.Write(output) { // Redisplay Mark / Selected Card Pos := &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[1] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output()) } } } else { break } } if err == nil { // Not a timeout /* if r < 0x1000 { // not a function key r = int(unicode.ToUpper(rune(r))) } */ switch ex { case door.LEFT_ARROW: r = '4' case door.RIGHT_ARROW: r = '6' } switch r { case '\x0d': // Next Card var updateStreakPanel bool = false if pc.Current_streak > pc.Hand_streak { pc.Hand_streak = pc.Current_streak updateStreakPanel = true } if pc.Current_streak > pc.Month_streak { pc.Month_streak = pc.Current_streak updateStreakPanel = true } if pc.Current_streak > pc.Best_streak { pc.Best_streak = pc.Current_streak save_streak = true updateStreakPanel = true // pc.Door.Write(pc.StreakPanel.Update()) } if updateStreakPanel { pc.Door.Write(pc.StreakPanel.Update()) } if pc.Play_card < 51 { pc.Play_card++ pc.Current_streak = 0 pc.Door.Write(pc.StreakPanel.Update()) pc.Door.Write(pc.LeftPanel.Update()) // Deal the next card if pc.Play_card == 51 { // out of cards cpos := &CardPos[29] c = &pc.DeckPanel.Backs[0] c.X = cpos.X + pc.Off_X c.Y = cpos.Y + pc.Off_Y pc.Door.Write(c.Output()) } cpos := &CardPos[28] c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]] c.X = cpos.X + pc.Off_X c.Y = cpos.Y + pc.Off_Y pc.Door.Write(c.Output()) } case 'R', 'r': pc.Redraw(false) case 'Q', 'q': // Possibly prompt here for [N]ext hand or [Q]uit // Odd: It seems like streaks should be tracked in // one place -- when we play a card... if pc.Current_streak > pc.Best_streak { pc.Best_streak = pc.Current_streak pc.Door.Write(pc.StreakPanel.Update()) } pc.NextQuitPanel.Update() pc.Door.Write(pc.NextQuitPanel.Output()) if pc.State[26] == 2 { pc.Door.Write(door.ColorText("BLACK")) } else { pc.Door.Write(door.Reset) } if pc.Hand < pc.Total_hands { r = pc.Door.GetOneOf("CNQ") } else { r = pc.Door.GetOneOf("CDQ") } if r == 'C' { // Continue pc.Redraw(false) } else { if r == 'D' || r == 'Q' { if pc.Score >= 50 { pc.DB.SaveScore(NewYMDFromTime(time.Now()), pc.Play_day, pc.Hand, 0, pc.Hand_streak, pc.Score) } in_game = false } else { if r == 'N' { pc.DB.SaveScore(NewYMDFromTime(time.Now()), pc.Play_day, pc.Hand, 0, pc.Hand_streak, pc.Score) pc.Hand++ goto Next_Hand } } } case ' ', '5': if CanPlay(pc.Deck[pc.Select_card], pc.Deck[pc.Play_card]) { pc.Current_streak++ pc.Door.Write(pc.StreakPanel.Update()) pc.Score += 10 if pc.Current_streak > 1 { pc.Score += pc.Current_streak * 5 } pc.Door.Write(pc.ScorePanel.Update()) // Play card pc.State[pc.Select_card] = 2 { // Swap out the select card with the play card temp := pc.Deck[pc.Select_card] pc.Deck[pc.Select_card] = pc.Deck[pc.Play_card] pc.Deck[pc.Play_card] = temp // Select card is invalidated here. Find new card. check := Unblocks(pc.Select_card) var left bool = false var right bool = false for _, chk := range check { blk := Blocks[chk] if blk[0] == pc.Select_card { right = true } if blk[1] == pc.Select_card { left = true } } cardback_color := pc.DeckPanel.Backs[1].Lines[0].DefaultColor pc.Door.Write(RemoveCard(pc.Select_card, cardback_color, pc.Off_X, pc.Off_Y, left, right)) // Redraw play card #28 ("old" select_card) cpos := &CardPos[28] c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]] c.X = cpos.X + pc.Off_X c.Y = cpos.Y + pc.Off_Y pc.Door.Write(c.Output()) // Did we unhide a card? var new_card_shown int = -1 if len(check) > 0 { for _, chk := range check { blk := Blocks[chk] if pc.State[blk[0]] == 2 && pc.State[blk[1]] == 2 { pc.State[chk] = 1 cpos := &CardPos[chk] c = &pc.DeckPanel.Cards[pc.Deck[chk]] c.X = cpos.X + pc.Off_X c.Y = cpos.Y + pc.Off_Y pc.Door.Write(c.Output()) new_card_shown = chk } } } else { // top card cleared cpos := &CardPos[pc.Select_card] pc.Door.Write(door.Goto(cpos.X+pc.Off_X, cpos.Y+pc.Off_Y) + Bonus()) pc.Score += 100 pc.State[pc.Select_card] = 3 // Handle in "redraw" pc.Door.Write(pc.ScorePanel.Update()) } // Find new number for select_card. if new_card_shown != -1 { pc.Select_card = new_card_shown } else { new_select := FindClosestActiveCard(&pc.State, pc.Select_card) if new_select != -1 { pc.Select_card = new_select } else { log.Println("Winner") pc.Select_card = -1 pc.Score += 15 * (51 - pc.Play_card) pc.Door.Write(pc.ScorePanel.Update()) // Save Score pc.DB.SaveScore(NewYMDFromTime(time.Now()), pc.Play_day, pc.Hand, 1, pc.Hand_streak, pc.Score) pc.NextQuitPanel.Update() pc.Door.Write(pc.NextQuitPanel.Output()) if pc.State[26] == 2 { pc.Door.Write(door.ColorText("BLACK")) } else { pc.Door.Write(door.Reset) } if pc.Hand < pc.Total_hands { r = pc.Door.GetOneOf("NQ") } else { r = pc.Door.GetOneOf("DQ") } if r == 'N' { pc.Hand++ goto Next_Hand } in_game = false // if r == 'D' ? if r == 'Q' { // this seemed odd. We quit the game ? r = 'Q' } break } } // Update the select_card marker Pos := &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[1] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output() + door.Reset) } } case '4': var new_select int = FindNextActiveCard(true, &pc.State, pc.Select_card) if new_select >= 0 { Pos := &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[0] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output()) pc.Select_card = new_select Pos = &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[1] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output() + door.Reset) } case '6': var new_select int = FindNextActiveCard(false, &pc.State, pc.Select_card) if new_select >= 0 { Pos := &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[0] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output()) pc.Select_card = new_select Pos = &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[1] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output() + door.Reset) } } } else { in_game = false } } return r } func (pc *PlayCards) Redraw(Dealing bool) { // stars.display() pc.Door.Write(door.Clrscr + door.Reset) pc.Door.Write(pc.SpaceAceTriPeaks.Output()) var c *door.Panel { // Step 1: Draw the deck "source" pos := &CardPos[29] if pc.Play_card == 51 { // out of cards pos.Level = 0 } c = &pc.DeckPanel.Backs[pos.Level] c.X = pos.X + pc.Off_X c.Y = pos.Y + pc.Off_Y pc.LeftPanel.X = pos.X + pc.Off_X pc.LeftPanel.Y = pos.Y + pc.Off_Y + 3 // const height see deck pc.ScorePanel.Update() pc.LeftPanel.Update() pc.StreakPanel.Update() pc.CmdPanel.Update() pc.Door.Write(pc.ScorePanel.Output()) pc.Door.Write(pc.LeftPanel.Output()) pc.Door.Write(pc.StreakPanel.Output()) pc.Door.Write(pc.CmdPanel.Output()) pc.Door.Write(c.Output()) if Dealing { time.Sleep(time.Second) } } var x_max int = 29 if Dealing { x_max = 28 } for x := 0; x < x_max; x++ { pos := &CardPos[x] if Dealing { time.Sleep(time.Duration(75) * time.Millisecond) } // log.Printf("Redraw(%d, %d)", x, pos.Level) if Dealing { c = &pc.DeckPanel.Backs[pos.Level] c.X = pos.X + pc.Off_X c.Y = pos.Y + pc.Off_Y pc.Door.Write(c.Output()) } else { switch pc.State[x] { case 0: c = &pc.DeckPanel.Backs[pos.Level] c.X = pos.X + pc.Off_X c.Y = pos.Y + pc.Off_Y pc.Door.Write(c.Output()) case 1: if x == 28 { c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]] } else { c = &pc.DeckPanel.Cards[pc.Deck[x]] } c.X = pos.X + pc.Off_X c.Y = pos.Y + pc.Off_Y pc.Door.Write(c.Output()) case 2: // no card to draw. case 3: // peak cleared, draw bonus var output string = door.Goto(pos.X+pc.Off_X, pos.Y+pc.Off_Y) output += Bonus() pc.Door.Write(output) } } } if Dealing { for x := 18; x < 29; x++ { pc.State[x] = 1 // CardPos[x] X Y Level Pos := &CardPos[x] time.Sleep(time.Duration(200) * time.Millisecond) c = &pc.DeckPanel.Cards[pc.Deck[x]] c.X = Pos.X + pc.Off_X c.Y = Pos.Y + pc.Off_Y pc.Door.Write(c.Output()) } } { Pos := &CardPos[pc.Select_card] c = &pc.DeckPanel.Mark[1] c.X = Pos.X + pc.Off_X + 2 c.Y = Pos.Y + pc.Off_Y + 2 pc.Door.Write(c.Output()) } } func Bonus() string { return door.ColorText("BOLD YELLOW") + "BONUS" } func (pc *PlayCards) UpdateCalendarDays() { }