playcards.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254
  1. package main
  2. import (
  3. "crypto/sha1"
  4. "encoding/binary"
  5. "fmt"
  6. "log"
  7. "math/rand"
  8. "red-green/door"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. func StringToANSIColor(colorCode string) string {
  14. colors := []string{
  15. "BLUE",
  16. "BROWN",
  17. "RED",
  18. "CYAN",
  19. "GREEN",
  20. "MAGENTA",
  21. "WHITE",
  22. }
  23. code := strings.ToUpper(colorCode)
  24. for _, c := range colors {
  25. if c == code {
  26. return door.ColorText(code)
  27. }
  28. }
  29. if code == "ALL" {
  30. rand.Seed(time.Now().UnixNano())
  31. pos := rand.Intn(len(colors))
  32. return door.ColorText(colors[pos])
  33. }
  34. return door.ColorText("WHITE")
  35. }
  36. type PlayCards struct {
  37. Door *door.Door
  38. DB *DBData
  39. Config *map[string]string
  40. RNG *rand.Rand
  41. Seeds []int32
  42. Total_hands int
  43. Play_card int
  44. Current_streak int
  45. Best_streak int
  46. Hand_streak int
  47. Month_streak int
  48. Select_card int
  49. Score int
  50. Play_day YMD
  51. Days_played int
  52. Hand int
  53. SpaceAceTriPeaks door.Panel
  54. ScorePanel door.Panel
  55. StreakPanel door.Panel
  56. LeftPanel door.Panel
  57. CmdPanel door.Panel
  58. NextQuitPanel door.Panel
  59. DeckPanel Deck
  60. Deck []DeckType // [51]DeckType t
  61. State []DeckType // [51]DeckType
  62. Off_X int
  63. Off_Y int
  64. Calendar door.Screen
  65. Calendar_panel_days [42]int // Where is each day positioned on the Calendar? 42 = 7 day * 6 lines
  66. Calendar_day_status [31]int
  67. }
  68. // Calendar_day [31]DBDate // deprecate
  69. // Possibly change Deck to [51]DeckType. This game always only has 1 deck.
  70. // Possibly change State [51]DeckType to [51]int8.
  71. // There are few states to track.
  72. func cmdLineRender(bracket string, inner string, outer string) func(string) string {
  73. return func(input string) string {
  74. var r door.Render = door.Render{Line: input}
  75. inOuter := true
  76. for _, c := range input {
  77. if c == '[' {
  78. inOuter = false
  79. r.Append(bracket, 1)
  80. continue
  81. }
  82. if c == ']' {
  83. inOuter = true
  84. r.Append(bracket, 1)
  85. continue
  86. }
  87. if inOuter {
  88. r.Append(outer, 1)
  89. } else {
  90. r.Append(inner, 1)
  91. }
  92. }
  93. return r.Result
  94. }
  95. }
  96. func (pc *PlayCards) InitValues() {
  97. hands := pc.DB.GetSetting("hands_per_day", "3")
  98. pc.Total_hands, _ = strconv.Atoi(hands)
  99. pc.Play_card = 28
  100. pc.Current_streak = 0
  101. pc.Best_streak = 0
  102. best := pc.DB.GetSetting("best_streak", "0")
  103. pc.Best_streak, _ = strconv.Atoi(best)
  104. pc.Month_streak = 0
  105. // Init the best streak for this month
  106. // TODO: Pull from DB.
  107. pc.Hand_streak = 0
  108. // Best for this hand
  109. pc.Select_card = 23
  110. pc.Score = 0
  111. }
  112. func (pc *PlayCards) Init() {
  113. // init_values()
  114. pc.InitValues()
  115. // PlayCards::PlayCards()
  116. pc.Play_day = NewYMDFromTime(time.Now())
  117. // NormalizeDate(&pc.Play_day)
  118. //pc.Play_day_t = pc.Play_day.Unix()
  119. pc.DeckPanel.Init()
  120. var last_played YMD
  121. last := pc.DB.GetSetting("last_played", "0")
  122. temp, _ := strconv.ParseInt(last, 10, 64)
  123. last_played = YMD(temp)
  124. days := pc.DB.GetSetting("days_played", "0")
  125. pc.Days_played, _ = strconv.Atoi(days)
  126. if last_played != pc.Play_day {
  127. pc.DB.SetSetting("last_played", strconv.FormatInt(int64(pc.Play_day), 10))
  128. pc.DB.SetSetting("days_played", "0")
  129. pc.Days_played = 0
  130. }
  131. pc.Seeds = make([]int32, 0)
  132. // config _seed
  133. parts := strings.Split((*pc.Config)["_seed"], ",")
  134. for _, seed := range parts {
  135. i, _ := strconv.ParseInt(seed, 10, 32)
  136. pc.Seeds = append(pc.Seeds, int32(i))
  137. }
  138. pc.Hand = 0
  139. // spaceAceTriPeaks = make_tripeaks();
  140. {
  141. tripeakstext := " " + SPACEACE + " - Tri-Peaks Solitaire v" + SPACEACE_VERSION + " "
  142. pc.SpaceAceTriPeaks = door.Panel{Width: len(tripeakstext),
  143. Style: door.SINGLE,
  144. BorderColor: door.ColorText("CYAN ON BLACK"),
  145. }
  146. pc.SpaceAceTriPeaks.Lines = append(pc.SpaceAceTriPeaks.Lines,
  147. door.Line{Text: tripeakstext})
  148. }
  149. svRender := RenderStatusValue(door.ColorText("BOLD WHITE ON BLUE"),
  150. door.ColorText("BOLD YELLOW ON BLUE"))
  151. // score_panel = make_score_panel();
  152. {
  153. W := 25
  154. pc.ScorePanel = door.Panel{Width: W}
  155. text := "Name: " + pc.Door.Config.Real_name
  156. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  157. door.Line{Text: fmt.Sprintf("%*s", -W, text), RenderF: svRender})
  158. scoreUpdate := func() string {
  159. txt := fmt.Sprintf("Score: %d", pc.Score)
  160. txt += strings.Repeat(" ", W-len(txt))
  161. return txt
  162. }
  163. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  164. door.Line{Text: scoreUpdate(),
  165. UpdateF: scoreUpdate,
  166. RenderF: svRender,
  167. })
  168. timeUpdate := func() string {
  169. var left int = int(pc.Door.TimeLeft().Minutes())
  170. var used int = int(pc.Door.TimeUsed().Minutes())
  171. txt := fmt.Sprintf("Time used: %3d / %3d", used, left)
  172. txt += strings.Repeat(" ", W-len(txt))
  173. return txt
  174. }
  175. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  176. door.Line{Text: timeUpdate(),
  177. UpdateF: timeUpdate,
  178. RenderF: svRender,
  179. })
  180. handUpdate := func() string {
  181. txt := fmt.Sprintf("Playing Hand %d of %d", pc.Hand, pc.Total_hands)
  182. txt += strings.Repeat(" ", W-len(txt))
  183. return txt
  184. }
  185. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  186. door.Line{Text: handUpdate(),
  187. UpdateF: handUpdate,
  188. RenderF: svRender,
  189. })
  190. }
  191. // streak_panel = make_streak_panel();
  192. {
  193. W := 20
  194. pc.StreakPanel = door.Panel{Width: W}
  195. dateUpdate := func() string {
  196. format, ok := (*pc.Config)["date_format"]
  197. if !ok {
  198. format = "January 2"
  199. }
  200. txt := fmt.Sprintf("Playing: %s", ToTime(pc.Play_day).Format(format))
  201. txt += strings.Repeat(" ", W-len(txt))
  202. return txt
  203. }
  204. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  205. door.Line{Text: dateUpdate(),
  206. UpdateF: dateUpdate,
  207. RenderF: svRender,
  208. })
  209. currentUpdate := func() string {
  210. txt := fmt.Sprintf("Current Streak: %d", pc.Current_streak)
  211. txt += strings.Repeat(" ", W-len(txt))
  212. return txt
  213. }
  214. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  215. door.Line{Text: currentUpdate(),
  216. UpdateF: currentUpdate,
  217. RenderF: svRender,
  218. })
  219. handUpdate := func() string {
  220. txt := fmt.Sprintf("Hand Streak : %d", pc.Hand_streak)
  221. txt += strings.Repeat(" ", W-len(txt))
  222. return txt
  223. }
  224. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  225. door.Line{Text: handUpdate(),
  226. UpdateF: handUpdate,
  227. RenderF: svRender,
  228. })
  229. longestUpdate := func() string {
  230. txt := fmt.Sprintf("Longest Streak: %d", pc.Best_streak)
  231. txt += strings.Repeat(" ", W-len(txt))
  232. return txt
  233. }
  234. // TODO:
  235. // Add: This Month and This Hand values
  236. // pc.Month_streak, pc.Hand_streak
  237. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  238. door.Line{Text: longestUpdate(),
  239. UpdateF: longestUpdate,
  240. RenderF: svRender,
  241. })
  242. }
  243. // left_panel = make_left_panel();
  244. {
  245. W := 13
  246. pc.LeftPanel = door.Panel{Width: W}
  247. leftUpdate := func() string {
  248. txt := fmt.Sprintf("Cards left:%d", 51-pc.Play_card)
  249. txt += strings.Repeat(" ", W-len(txt))
  250. return txt
  251. }
  252. pc.LeftPanel.Lines = append(pc.LeftPanel.Lines,
  253. door.Line{Text: leftUpdate(),
  254. UpdateF: leftUpdate,
  255. RenderF: svRender,
  256. })
  257. }
  258. // cmd_panel = make_command_panel();
  259. {
  260. W := 76
  261. pc.CmdPanel = door.Panel{Width: W}
  262. var commands string
  263. if door.Unicode {
  264. commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] Draw [Q]uit [R]edraw [H]elp"
  265. commands += strings.Repeat(" ", W-len([]rune(commands)))
  266. } else {
  267. commands = "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit [R]edraw [H]elp"
  268. commands += strings.Repeat(" ", W-len(commands))
  269. }
  270. cmdRender := cmdLineRender(door.ColorText("BOLD YELLOW ON BLUE"),
  271. door.ColorText("BOLD CYAN ON BLUE"),
  272. door.ColorText("BOLD GREEN ON BLUE"))
  273. pc.CmdPanel.Lines = append(pc.CmdPanel.Lines,
  274. door.Line{Text: commands,
  275. RenderF: cmdRender})
  276. }
  277. // next_quit_panel = make_next_panel();
  278. {
  279. W := 50
  280. pc.NextQuitPanel = door.Panel{Width: W,
  281. Style: door.DOUBLE,
  282. BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
  283. }
  284. nextUpdate := func() string {
  285. var text string
  286. if pc.Select_card != -1 {
  287. text = "[C]ontinue this hand"
  288. }
  289. if pc.Hand < pc.Total_hands {
  290. text += " [N]ext Hand [Q]uit"
  291. } else {
  292. text += " [D]one [Q]uit"
  293. }
  294. text = " " + text + " "
  295. text += strings.Repeat(" ", W-len(text))
  296. return text
  297. }
  298. pc.NextQuitPanel.Lines = append(pc.NextQuitPanel.Lines,
  299. door.Line{Text: nextUpdate(),
  300. UpdateF: nextUpdate,
  301. RenderF: cmdLineRender(door.ColorText("BOLD YEL ON BLU"),
  302. door.ColorText("BOLD CYAN ON BLU"),
  303. door.ColorText("BOLD GREE ON BLUE"))})
  304. }
  305. // calendar = make_calendar();
  306. {
  307. pc.Calendar = door.Screen{}
  308. var month YMD = NewYMDFromTime(time.Now())
  309. month.First()
  310. var today_day int = month.Day()
  311. // FirstOfMonthDate(&month)
  312. var month_end YMD = month
  313. month_end.Last()
  314. // clear out
  315. /*
  316. for x := range pc.Calendar_day {
  317. pc.Calendar_day[x] = 0
  318. }
  319. // pc.Calendar_day_t = make([]int64, 31)
  320. pc.Calendar_day[0] = month // .Unix()
  321. */
  322. var First_Weekday int = int(ToTime(month).Weekday())
  323. var month_last_day = month_end.Day()
  324. /*
  325. var calendarDay DBDate = month
  326. for x := 1; x <= month_last_day; x++ {
  327. calendarDay.SetDay(x) // = DBDate((int(calendarDay) % 100) + x)
  328. pc.Calendar_day[x-1] = calendarDay
  329. }
  330. */
  331. // var month_end time.Time = month
  332. /*
  333. for {
  334. month_end = month_end.AddDate(0, 0, 1)
  335. NormalizeDate(&month_end)
  336. if month_end.Day() == 1 {
  337. break
  338. } else {
  339. pc.Calendar_day_t[month_end.Day()-1] = month_end.Unix()
  340. month_last_day = month_end.Day()
  341. }
  342. }
  343. */
  344. // clear out
  345. for x := range pc.Calendar_panel_days {
  346. pc.Calendar_panel_days[x] = 0
  347. }
  348. // pc.Calendar_panel_days = make([]int, 6*7)
  349. var row int = 0
  350. for x := 0; x < month_last_day; x++ {
  351. var dow int = (x + First_Weekday) % 7
  352. if x != 0 && dow == 0 {
  353. row++
  354. }
  355. pc.Calendar_panel_days[row*7+dow] = x + 1
  356. }
  357. // clear out
  358. for x := range pc.Calendar_day_status {
  359. pc.Calendar_day_status[x] = 0
  360. }
  361. // pc.Calendar_day_status = make([]int, 31)
  362. last_played := pc.DB.WhenPlayed()
  363. for played, hands := range last_played {
  364. if played >= month {
  365. // Ok, it is within the range
  366. var played_day int = played.Day()
  367. if hands < pc.Total_hands {
  368. pc.Calendar_day_status[played_day-1] = 1
  369. } else {
  370. pc.Calendar_day_status[played_day-1] = 2
  371. }
  372. }
  373. }
  374. // Get play_days_ahead from config
  375. var play_days_ahead int = 0
  376. if playdays, has := (*pc.Config)["play_days_ahead"]; has {
  377. play_days_ahead, _ = strconv.Atoi(playdays)
  378. }
  379. // Mark all days ahead as NNY.
  380. for d := 0; d < 31; d++ {
  381. if today_day+play_days_ahead-1 < d {
  382. pc.Calendar_day_status[d] = 3
  383. }
  384. }
  385. // Get current month as string
  386. var current string = strings.ToUpper(CurrentMonth(ToTime(month)))
  387. if len(current) < 6 {
  388. current = " " + current + " "
  389. }
  390. // FUTURE: Replace this with TDF rendering of Month.
  391. // make_month
  392. var MonthPanel door.Panel = door.Panel{X: 3,
  393. Y: 3,
  394. Width: 3,
  395. Style: door.DOUBLE,
  396. BorderColor: door.ColorText("BOLD YELLOW ON BLACK"),
  397. }
  398. for _, c := range current {
  399. MonthPanel.Lines = append(MonthPanel.Lines,
  400. door.Line{Text: fmt.Sprintf(" %c ", c)})
  401. }
  402. pc.Calendar.AddPanel(MonthPanel)
  403. // make_weekday
  404. var WeekdayPanel door.Panel = door.Panel{X: 8,
  405. Y: 3,
  406. Width: 41,
  407. Style: door.DOUBLE,
  408. BorderColor: door.ColorText("BOLD CYAN ON BLACK"),
  409. }
  410. WeekdayPanel.Lines = append(WeekdayPanel.Lines,
  411. door.Line{Text: " SUN MON TUE WED THU FRI SAT "})
  412. pc.Calendar.AddPanel(WeekdayPanel)
  413. // make_calendar_panel
  414. var CalendarPanel door.Panel
  415. {
  416. W := 41
  417. CalendarPanel = door.Panel{Width: W,
  418. Style: door.DOUBLE,
  419. BorderColor: door.ColorText("CYAN ON BLACK")}
  420. calendarRender := func(text string) string {
  421. // var result string
  422. // var lastColor string
  423. result := door.Render{Line: text}
  424. digits := door.ColorText("CYAN ON BLACK")
  425. digits_play := door.ColorText("BOLD CYAN ON BLACK")
  426. spaces := door.ColorText("WHITE ON BLACK")
  427. open := door.ColorText("BOLD GREEN ON BLACK")
  428. hands := door.ColorText("BOLD YELLOW ON BLACK")
  429. full := door.ColorText("RED ON BLACK")
  430. nny := door.ColorText("BOLD BLACK ON BLACK")
  431. for days := 0; days < 7; days++ {
  432. var dayText string
  433. if days == 0 {
  434. result.Append(spaces, 1)
  435. }
  436. dayText = text[1+days*6 : (1+days*6)+3]
  437. // if dayText[1] == ' ' {
  438. if dayText[0] == ' ' {
  439. result.Append(spaces, 3)
  440. } else {
  441. // Something is here
  442. cday := dayText[2]
  443. if cday == 'o' || cday == 'h' {
  444. result.Append(digits_play, 2)
  445. } else {
  446. result.Append(digits, 2)
  447. }
  448. switch cday {
  449. case 'o':
  450. result.Append(open, 1)
  451. case 'h':
  452. result.Append(hands, 1)
  453. case 'x':
  454. result.Append(full, 1)
  455. case 'u':
  456. result.Append(nny, 1)
  457. }
  458. }
  459. if days == 6 {
  460. result.Append(spaces, 1)
  461. } else {
  462. result.Append(spaces, 3)
  463. }
  464. }
  465. return result.Result
  466. }
  467. for row := 0; row < 6; row++ {
  468. // Do this, or it doesn't get captured correctly.
  469. _row := row
  470. // _pc := pc
  471. calendarUpdate := func() string {
  472. var text string
  473. for d := 0; d < 7; d++ {
  474. text += " "
  475. v := pc.Calendar_panel_days[(_row*7)+d]
  476. if v == 0 {
  477. text += " "
  478. } else {
  479. text += fmt.Sprintf("%-2d", v)
  480. status := pc.Calendar_day_status[v-1]
  481. switch status {
  482. case 0:
  483. text += "o"
  484. case 1:
  485. text += "h"
  486. case 2:
  487. text += "x"
  488. case 3:
  489. text += "u"
  490. }
  491. }
  492. if d == 6 {
  493. text += " "
  494. } else {
  495. text += " "
  496. }
  497. }
  498. return text
  499. }
  500. log.Printf("line %d: [%s]\n", row, calendarUpdate())
  501. CalendarPanel.Lines = append(CalendarPanel.Lines,
  502. door.Line{Text: calendarUpdate(),
  503. UpdateF: calendarUpdate,
  504. RenderF: calendarRender})
  505. }
  506. }
  507. CalendarPanel.X = 8
  508. CalendarPanel.Y = 6
  509. pc.Calendar.AddPanel(CalendarPanel)
  510. }
  511. // end make_calendar
  512. }
  513. func (pc *PlayCards) Play() rune {
  514. // this defaults (for now) to playing today (if unplayed).
  515. // Otherwise display calendar.
  516. pc.Play_day = NewYMDFromTime(time.Now())
  517. // NormalizeDate(&pc.Play_day)
  518. // pc.Play_day_t = pc.Play_day.Unix()
  519. played := pc.DB.HandsPlayedOnDay(pc.Play_day)
  520. log.Printf("HandsPlayedOnDay(%d), %d", pc.Play_day, played)
  521. var r rune
  522. if played == 0 {
  523. pc.Door.Write(door.CRNL + door.Reset + "Let's play today..." + door.CRNL)
  524. time.Sleep(time.Second * time.Duration(2))
  525. pc.Hand = 1
  526. r = pc.PlayCards()
  527. if r != 'D' {
  528. return r
  529. }
  530. } else {
  531. if played < pc.Total_hands {
  532. // let's finish today...
  533. pc.Door.Write(door.CRNL + door.Reset + "Let's finish today..." + door.CRNL)
  534. time.Sleep(time.Second * time.Duration(2))
  535. pc.Hand = played + 1
  536. r = pc.PlayCards()
  537. if r != 'D' {
  538. return r
  539. }
  540. }
  541. }
  542. CALENDAR_UPDATE:
  543. // pc.UpdateCalendarDays()
  544. month_t := pc.Play_day // pc.Calendar_day[0]
  545. month_t.SetDay(1)
  546. last_played := pc.DB.WhenPlayed()
  547. for played, hands := range last_played {
  548. if played >= month_t {
  549. // Ok, it is within the range
  550. var played_day int = played.Day()
  551. log.Printf("update: month_unix %d, played %d, hands %d (total %d)\n",
  552. month_t, played_day, hands, pc.Total_hands)
  553. if hands < pc.Total_hands {
  554. pc.Calendar_day_status[played_day-1] = 1
  555. } else {
  556. pc.Calendar_day_status[played_day-1] = 2
  557. }
  558. }
  559. }
  560. // log.Printf("%#v\n", pc.Calendar)
  561. log.Println("Calendar.Update()")
  562. pc.Calendar.Update()
  563. log.Println("Calendar.Output()")
  564. pc.Door.Write(pc.Calendar.Output())
  565. var has_playable_day bool = false
  566. for x := 0; x < 31; x++ {
  567. status := pc.Calendar_day_status[x]
  568. if status == 0 || status == 1 {
  569. has_playable_day = true
  570. break
  571. }
  572. }
  573. if !has_playable_day {
  574. pc.Door.Write(door.CRNL + "Sorry, there are no days available to play." + door.CRNL)
  575. r = press_a_key(pc.Door)
  576. if r < 0 {
  577. return r
  578. } else {
  579. return 'Q'
  580. }
  581. }
  582. log.Println("Choose Day")
  583. pc.Door.Write(door.CRNL + "Please choose a day : " + door.SavePos)
  584. AGAIN:
  585. log.Println("Input")
  586. var toplay string = pc.Door.Input(3)
  587. log.Printf("Input was: %s\n", toplay)
  588. pc.Door.Write(door.RestorePos)
  589. var number int
  590. var err error
  591. number, err = strconv.Atoi(toplay)
  592. if err != nil {
  593. number = 0
  594. }
  595. if number == 0 {
  596. return ' '
  597. }
  598. var status int
  599. if number <= 31 {
  600. status = pc.Calendar_day_status[number-1]
  601. if status == 0 {
  602. // play full day
  603. pc.Hand = 1
  604. pc.Play_day.SetDay(number) // = pc.Calendar_day[number-1]
  605. // pc.Play_day = time.Unix(pc.Play_day_t, 0)
  606. r = pc.PlayCards()
  607. if r == 'D' {
  608. goto CALENDAR_UPDATE
  609. }
  610. return r
  611. }
  612. if status == 1 {
  613. // Play half day
  614. pc.Play_day.SetDay(number) // = pc.Calendar_day[number-1]
  615. // pc.Play_day = time.Unix(pc.Play_day_t, 0)
  616. played := pc.DB.HandsPlayedOnDay(pc.Play_day)
  617. if played < pc.Total_hands {
  618. pc.Hand = played + 1
  619. r = pc.PlayCards()
  620. if r == 'D' {
  621. goto CALENDAR_UPDATE
  622. }
  623. return r
  624. }
  625. }
  626. goto AGAIN
  627. }
  628. return ' '
  629. }
  630. func (pc *PlayCards) PlayCards() rune {
  631. pc.InitValues()
  632. var game_width int
  633. var game_height int = 20
  634. // pos := &CardPos[27]
  635. game_width = CardPos[27].X + 5
  636. MX := door.Width
  637. MY := door.Height
  638. pc.Off_X = (MX - game_width) / 2
  639. pc.Off_Y = (MY - game_height) / 2
  640. // _ = off_x
  641. // We can now position things properly centered
  642. pc.SpaceAceTriPeaks.X = (MX - pc.SpaceAceTriPeaks.Width) / 2
  643. pc.SpaceAceTriPeaks.Y = pc.Off_Y
  644. pc.Off_Y += 3
  645. currentDefault := pc.DB.GetSetting("DeckColor", "ALL")
  646. Next_Hand:
  647. deck_color := StringToANSIColor(currentDefault)
  648. pc.DeckPanel.SetBackColor(deck_color)
  649. pc.Play_card = 28
  650. pc.Select_card = 23
  651. pc.Score = 0
  652. pc.Current_streak = 0
  653. pc.Hand_streak = 0
  654. // Don't touch Month_streak.
  655. // Use play day to seed RNG
  656. {
  657. // Secret Squirrel method of seeding the RNG
  658. seed_seq := make([]byte, 0)
  659. for _, seed := range pc.Seeds {
  660. ba := Int32toByteArray(seed)
  661. seed_seq = append(seed_seq, ba[:]...)
  662. // sha1.Sum(ba[:])
  663. }
  664. pd := Int64toByteArray(int64(pc.Play_day))
  665. seed_seq = append(seed_seq, pd[:]...)
  666. // We also need the hand # that we're playing.
  667. seed_seq = append(seed_seq, byte(pc.Hand))
  668. result := sha1.Sum(seed_seq)
  669. var seed int64 = int64(binary.BigEndian.Uint64(result[0:8]))
  670. pc.RNG.Seed(seed)
  671. // I'm seeing changes in the seed_seq bytes, but the seed is the same number.
  672. // log.Printf("%#v\nSeed %d\nLen %d\nresult %#v\n", seed_seq, seed, len(seed_seq), result)
  673. pc.Deck = ShuffleCards(pc.RNG, 1)
  674. pc.State = MakeCardStates(1)
  675. }
  676. // Position the panels
  677. {
  678. off_yp := pc.Off_Y + 11
  679. left_panel_x := CardPos[18].X
  680. right_panel_x := CardPos[15].X
  681. pc.ScorePanel.X = left_panel_x + pc.Off_X
  682. pc.ScorePanel.Y = off_yp
  683. pc.StreakPanel.X = right_panel_x + pc.Off_X
  684. pc.StreakPanel.Y = off_yp
  685. pc.CmdPanel.X = left_panel_x + pc.Off_X
  686. pc.CmdPanel.Y = off_yp + 5
  687. next_off_x := (MX - pc.NextQuitPanel.Width) / 2
  688. pc.NextQuitPanel.X = next_off_x
  689. pc.NextQuitPanel.Y = CardPos[10].Y + pc.Off_Y
  690. }
  691. var Dealing bool = true
  692. var r rune
  693. var ex door.Extended
  694. var err error
  695. pc.Redraw(Dealing)
  696. Dealing = false
  697. pc.LeftPanel.Update()
  698. pc.Door.Write(door.Reset)
  699. var c *door.Panel
  700. var in_game bool = true
  701. var save_streak bool = false
  702. var waiting int = 0
  703. for in_game {
  704. var output string
  705. output = pc.ScorePanel.Update()
  706. if output != "" {
  707. pc.Door.Write(output)
  708. {
  709. // Redisplay Mark / Selected Card
  710. Pos := &CardPos[pc.Select_card]
  711. c = &pc.DeckPanel.Mark[1]
  712. c.X = Pos.X + pc.Off_X + 2
  713. c.Y = Pos.Y + pc.Off_Y + 2
  714. pc.Door.Write(c.Output())
  715. }
  716. }
  717. if save_streak {
  718. save_streak = false
  719. pc.DB.SetSetting("best_streak", strconv.Itoa(pc.Best_streak))
  720. }
  721. waiting = 0
  722. for waiting < int(door.Inactivity) {
  723. r, ex, err = pc.Door.WaitKey(time.Second)
  724. if err == door.ErrTimeout {
  725. // TIMEOUT is expected here
  726. waiting++
  727. output = pc.ScorePanel.Update()
  728. if output != "" {
  729. pc.Door.Write(output)
  730. {
  731. // Redisplay Mark / Selected Card
  732. Pos := &CardPos[pc.Select_card]
  733. c = &pc.DeckPanel.Mark[1]
  734. c.X = Pos.X + pc.Off_X + 2
  735. c.Y = Pos.Y + pc.Off_Y + 2
  736. pc.Door.Write(c.Output())
  737. }
  738. }
  739. } else {
  740. break
  741. }
  742. }
  743. if err == nil {
  744. // Not a timeout
  745. /*
  746. if r < 0x1000 {
  747. // not a function key
  748. r = int(unicode.ToUpper(rune(r)))
  749. }
  750. */
  751. switch ex {
  752. case door.LEFT_ARROW:
  753. r = '4'
  754. case door.RIGHT_ARROW:
  755. r = '6'
  756. }
  757. switch r {
  758. case '\x0d':
  759. // Next Card
  760. var updateStreakPanel bool = false
  761. if pc.Current_streak > pc.Hand_streak {
  762. pc.Hand_streak = pc.Current_streak
  763. updateStreakPanel = true
  764. }
  765. if pc.Current_streak > pc.Month_streak {
  766. pc.Month_streak = pc.Current_streak
  767. updateStreakPanel = true
  768. }
  769. if pc.Current_streak > pc.Best_streak {
  770. pc.Best_streak = pc.Current_streak
  771. save_streak = true
  772. updateStreakPanel = true
  773. // pc.Door.Write(pc.StreakPanel.Update())
  774. }
  775. if updateStreakPanel {
  776. pc.Door.Write(pc.StreakPanel.Update())
  777. }
  778. if pc.Play_card < 51 {
  779. pc.Play_card++
  780. pc.Current_streak = 0
  781. pc.Door.Write(pc.StreakPanel.Update())
  782. pc.Door.Write(pc.LeftPanel.Update())
  783. // Deal the next card
  784. if pc.Play_card == 51 {
  785. // out of cards
  786. cpos := &CardPos[29]
  787. c = &pc.DeckPanel.Backs[0]
  788. c.X = cpos.X + pc.Off_X
  789. c.Y = cpos.Y + pc.Off_Y
  790. pc.Door.Write(c.Output())
  791. }
  792. cpos := &CardPos[28]
  793. c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]]
  794. c.X = cpos.X + pc.Off_X
  795. c.Y = cpos.Y + pc.Off_Y
  796. pc.Door.Write(c.Output())
  797. }
  798. case 'R', 'r':
  799. pc.Redraw(false)
  800. case 'Q', 'q':
  801. // Possibly prompt here for [N]ext hand or [Q]uit
  802. // Odd: It seems like streaks should be tracked in
  803. // one place -- when we play a card...
  804. if pc.Current_streak > pc.Best_streak {
  805. pc.Best_streak = pc.Current_streak
  806. pc.Door.Write(pc.StreakPanel.Update())
  807. }
  808. pc.NextQuitPanel.Update()
  809. pc.Door.Write(pc.NextQuitPanel.Output())
  810. if pc.State[26] == 2 {
  811. pc.Door.Write(door.ColorText("BLACK"))
  812. } else {
  813. pc.Door.Write(door.Reset)
  814. }
  815. if pc.Hand < pc.Total_hands {
  816. r = pc.Door.GetOneOf("CNQ")
  817. } else {
  818. r = pc.Door.GetOneOf("CDQ")
  819. }
  820. if r == 'C' {
  821. // Continue
  822. pc.Redraw(false)
  823. } else {
  824. if r == 'D' || r == 'Q' {
  825. if pc.Score >= 50 {
  826. pc.DB.SaveScore(NewYMDFromTime(time.Now()), pc.Play_day, pc.Hand, 0, pc.Hand_streak, pc.Score)
  827. }
  828. in_game = false
  829. } else {
  830. if r == 'N' {
  831. pc.DB.SaveScore(NewYMDFromTime(time.Now()), pc.Play_day, pc.Hand, 0, pc.Hand_streak, pc.Score)
  832. pc.Hand++
  833. goto Next_Hand
  834. }
  835. }
  836. }
  837. case ' ', '5':
  838. if CanPlay(pc.Deck[pc.Select_card],
  839. pc.Deck[pc.Play_card]) {
  840. pc.Current_streak++
  841. pc.Door.Write(pc.StreakPanel.Update())
  842. pc.Score += 10
  843. if pc.Current_streak > 1 {
  844. pc.Score += pc.Current_streak * 5
  845. }
  846. pc.Door.Write(pc.ScorePanel.Update())
  847. // Play card
  848. pc.State[pc.Select_card] = 2
  849. {
  850. // Swap out the select card with the play card
  851. temp := pc.Deck[pc.Select_card]
  852. pc.Deck[pc.Select_card] = pc.Deck[pc.Play_card]
  853. pc.Deck[pc.Play_card] = temp
  854. // Select card is invalidated here. Find new card.
  855. check := Unblocks(pc.Select_card)
  856. var left bool = false
  857. var right bool = false
  858. for _, chk := range check {
  859. blk := Blocks[chk]
  860. if blk[0] == pc.Select_card {
  861. right = true
  862. }
  863. if blk[1] == pc.Select_card {
  864. left = true
  865. }
  866. }
  867. cardback_color := pc.DeckPanel.Backs[1].Lines[0].DefaultColor
  868. pc.Door.Write(RemoveCard(pc.Select_card, cardback_color, pc.Off_X, pc.Off_Y, left, right))
  869. // Redraw play card #28 ("old" select_card)
  870. cpos := &CardPos[28]
  871. c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]]
  872. c.X = cpos.X + pc.Off_X
  873. c.Y = cpos.Y + pc.Off_Y
  874. pc.Door.Write(c.Output())
  875. // Did we unhide a card?
  876. var new_card_shown int = -1
  877. if len(check) > 0 {
  878. for _, chk := range check {
  879. blk := Blocks[chk]
  880. if pc.State[blk[0]] == 2 &&
  881. pc.State[blk[1]] == 2 {
  882. pc.State[chk] = 1
  883. cpos := &CardPos[chk]
  884. c = &pc.DeckPanel.Cards[pc.Deck[chk]]
  885. c.X = cpos.X + pc.Off_X
  886. c.Y = cpos.Y + pc.Off_Y
  887. pc.Door.Write(c.Output())
  888. new_card_shown = chk
  889. }
  890. }
  891. } else {
  892. // top card cleared
  893. cpos := &CardPos[pc.Select_card]
  894. pc.Door.Write(door.Goto(cpos.X+pc.Off_X, cpos.Y+pc.Off_Y) + Bonus())
  895. pc.Score += 100
  896. pc.State[pc.Select_card] = 3 // Handle in "redraw"
  897. pc.Door.Write(pc.ScorePanel.Update())
  898. }
  899. // Find new number for select_card.
  900. if new_card_shown != -1 {
  901. pc.Select_card = new_card_shown
  902. } else {
  903. new_select := FindClosestActiveCard(&pc.State, pc.Select_card)
  904. if new_select != -1 {
  905. pc.Select_card = new_select
  906. } else {
  907. log.Println("Winner")
  908. pc.Select_card = -1
  909. pc.Score += 15 * (51 - pc.Play_card)
  910. pc.Door.Write(pc.ScorePanel.Update())
  911. // Save Score
  912. pc.DB.SaveScore(NewYMDFromTime(time.Now()), pc.Play_day, pc.Hand, 1, pc.Hand_streak, pc.Score)
  913. pc.NextQuitPanel.Update()
  914. pc.Door.Write(pc.NextQuitPanel.Output())
  915. if pc.State[26] == 2 {
  916. pc.Door.Write(door.ColorText("BLACK"))
  917. } else {
  918. pc.Door.Write(door.Reset)
  919. }
  920. if pc.Hand < pc.Total_hands {
  921. r = pc.Door.GetOneOf("NQ")
  922. } else {
  923. r = pc.Door.GetOneOf("DQ")
  924. }
  925. if r == 'N' {
  926. pc.Hand++
  927. goto Next_Hand
  928. }
  929. in_game = false
  930. // if r == 'D' ?
  931. if r == 'Q' {
  932. // this seemed odd. We quit the game ?
  933. r = 'Q'
  934. }
  935. break
  936. }
  937. }
  938. // Update the select_card marker
  939. Pos := &CardPos[pc.Select_card]
  940. c = &pc.DeckPanel.Mark[1]
  941. c.X = Pos.X + pc.Off_X + 2
  942. c.Y = Pos.Y + pc.Off_Y + 2
  943. pc.Door.Write(c.Output() + door.Reset)
  944. }
  945. }
  946. case '4':
  947. var new_select int = FindNextActiveCard(true, &pc.State, pc.Select_card)
  948. if new_select >= 0 {
  949. Pos := &CardPos[pc.Select_card]
  950. c = &pc.DeckPanel.Mark[0]
  951. c.X = Pos.X + pc.Off_X + 2
  952. c.Y = Pos.Y + pc.Off_Y + 2
  953. pc.Door.Write(c.Output())
  954. pc.Select_card = new_select
  955. Pos = &CardPos[pc.Select_card]
  956. c = &pc.DeckPanel.Mark[1]
  957. c.X = Pos.X + pc.Off_X + 2
  958. c.Y = Pos.Y + pc.Off_Y + 2
  959. pc.Door.Write(c.Output() + door.Reset)
  960. }
  961. case '6':
  962. var new_select int = FindNextActiveCard(false, &pc.State, pc.Select_card)
  963. if new_select >= 0 {
  964. Pos := &CardPos[pc.Select_card]
  965. c = &pc.DeckPanel.Mark[0]
  966. c.X = Pos.X + pc.Off_X + 2
  967. c.Y = Pos.Y + pc.Off_Y + 2
  968. pc.Door.Write(c.Output())
  969. pc.Select_card = new_select
  970. Pos = &CardPos[pc.Select_card]
  971. c = &pc.DeckPanel.Mark[1]
  972. c.X = Pos.X + pc.Off_X + 2
  973. c.Y = Pos.Y + pc.Off_Y + 2
  974. pc.Door.Write(c.Output() + door.Reset)
  975. }
  976. }
  977. } else {
  978. in_game = false
  979. }
  980. }
  981. return r
  982. }
  983. func (pc *PlayCards) Redraw(Dealing bool) {
  984. // stars.display()
  985. pc.Door.Write(door.Clrscr + door.Reset)
  986. pc.Door.Write(pc.SpaceAceTriPeaks.Output())
  987. var c *door.Panel
  988. {
  989. // Step 1: Draw the deck "source"
  990. pos := &CardPos[29]
  991. if pc.Play_card == 51 {
  992. // out of cards
  993. pos.Level = 0
  994. }
  995. c = &pc.DeckPanel.Backs[pos.Level]
  996. c.X = pos.X + pc.Off_X
  997. c.Y = pos.Y + pc.Off_Y
  998. pc.LeftPanel.X = pos.X + pc.Off_X
  999. pc.LeftPanel.Y = pos.Y + pc.Off_Y + 3 // const height see deck
  1000. pc.ScorePanel.Update()
  1001. pc.LeftPanel.Update()
  1002. pc.StreakPanel.Update()
  1003. pc.CmdPanel.Update()
  1004. pc.Door.Write(pc.ScorePanel.Output())
  1005. pc.Door.Write(pc.LeftPanel.Output())
  1006. pc.Door.Write(pc.StreakPanel.Output())
  1007. pc.Door.Write(pc.CmdPanel.Output())
  1008. pc.Door.Write(c.Output())
  1009. if Dealing {
  1010. time.Sleep(time.Second)
  1011. }
  1012. }
  1013. var x_max int = 29
  1014. if Dealing {
  1015. x_max = 28
  1016. }
  1017. for x := 0; x < x_max; x++ {
  1018. pos := &CardPos[x]
  1019. if Dealing {
  1020. time.Sleep(time.Duration(75) * time.Millisecond)
  1021. }
  1022. // log.Printf("Redraw(%d, %d)", x, pos.Level)
  1023. if Dealing {
  1024. c = &pc.DeckPanel.Backs[pos.Level]
  1025. c.X = pos.X + pc.Off_X
  1026. c.Y = pos.Y + pc.Off_Y
  1027. pc.Door.Write(c.Output())
  1028. } else {
  1029. switch pc.State[x] {
  1030. case 0:
  1031. c = &pc.DeckPanel.Backs[pos.Level]
  1032. c.X = pos.X + pc.Off_X
  1033. c.Y = pos.Y + pc.Off_Y
  1034. pc.Door.Write(c.Output())
  1035. case 1:
  1036. if x == 28 {
  1037. c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]]
  1038. } else {
  1039. c = &pc.DeckPanel.Cards[pc.Deck[x]]
  1040. }
  1041. c.X = pos.X + pc.Off_X
  1042. c.Y = pos.Y + pc.Off_Y
  1043. pc.Door.Write(c.Output())
  1044. case 2:
  1045. // no card to draw.
  1046. case 3:
  1047. // peak cleared, draw bonus
  1048. var output string = door.Goto(pos.X+pc.Off_X, pos.Y+pc.Off_Y)
  1049. output += Bonus()
  1050. pc.Door.Write(output)
  1051. }
  1052. }
  1053. }
  1054. if Dealing {
  1055. for x := 18; x < 29; x++ {
  1056. pc.State[x] = 1
  1057. // CardPos[x] X Y Level
  1058. Pos := &CardPos[x]
  1059. time.Sleep(time.Duration(200) * time.Millisecond)
  1060. c = &pc.DeckPanel.Cards[pc.Deck[x]]
  1061. c.X = Pos.X + pc.Off_X
  1062. c.Y = Pos.Y + pc.Off_Y
  1063. pc.Door.Write(c.Output())
  1064. }
  1065. }
  1066. {
  1067. Pos := &CardPos[pc.Select_card]
  1068. c = &pc.DeckPanel.Mark[1]
  1069. c.X = Pos.X + pc.Off_X + 2
  1070. c.Y = Pos.Y + pc.Off_Y + 2
  1071. pc.Door.Write(c.Output())
  1072. }
  1073. }
  1074. func Bonus() string {
  1075. return door.ColorText("BOLD YELLOW") + "BONUS"
  1076. }
  1077. func (pc *PlayCards) UpdateCalendarDays() {
  1078. }