playcards.go 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  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. Select_card int
  47. Score int
  48. Play_day time.Time
  49. Play_day_t int64
  50. Days_played int
  51. Hand int
  52. Day_status [42]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. CalendarPanel door.Panel
  60. DeckPanel Deck
  61. Deck []DeckType
  62. State []DeckType
  63. Off_X int
  64. Off_Y int
  65. Calendar_day_t [31]int64
  66. Calendar door.Screen
  67. }
  68. // Adjust date to 2:00 AM
  69. func NormalizeDate(date *time.Time) {
  70. offset := time.Duration(date.Second())*time.Second +
  71. time.Duration(date.Minute())*time.Minute +
  72. time.Duration(date.Hour()-2)*time.Hour +
  73. time.Duration(date.Nanosecond())*time.Nanosecond
  74. *date = date.Add(-offset)
  75. }
  76. // Find the 1st of the Month (Normalized) 2:00 AM
  77. func FirstOfMonthDate(date *time.Time) {
  78. if date.Day() != 1 {
  79. *date = date.AddDate(0, 0, 1-date.Day())
  80. }
  81. NormalizeDate(date)
  82. }
  83. func cmdLineRender(bracket string, inner string, outer string) func(string) string {
  84. return func(input string) string {
  85. var r door.Render = door.Render{Line: input}
  86. inOuter := true
  87. for _, c := range input {
  88. if c == '[' {
  89. inOuter = false
  90. r.Append(bracket, 1)
  91. continue
  92. }
  93. if c == ']' {
  94. inOuter = true
  95. r.Append(bracket, 1)
  96. continue
  97. }
  98. if inOuter {
  99. r.Append(outer, 1)
  100. } else {
  101. r.Append(inner, 1)
  102. }
  103. }
  104. return r.Result
  105. }
  106. }
  107. func (pc *PlayCards) InitValues() {
  108. hands := pc.DB.GetSetting("hands_per_day", "3")
  109. pc.Total_hands, _ = strconv.Atoi(hands)
  110. pc.Play_card = 28
  111. pc.Current_streak = 0
  112. pc.Best_streak = 0
  113. best := pc.DB.GetSetting("best_streak", "0")
  114. pc.Best_streak, _ = strconv.Atoi(best)
  115. pc.Select_card = 23
  116. pc.Score = 0
  117. }
  118. func (pc *PlayCards) Init() {
  119. // init_values()
  120. pc.InitValues()
  121. // PlayCards::PlayCards()
  122. pc.Play_day = time.Now()
  123. NormalizeDate(&pc.Play_day)
  124. pc.Play_day_t = pc.Play_day.Unix()
  125. pc.DeckPanel.Init()
  126. var last_played int64
  127. last := pc.DB.GetSetting("last_played", "0")
  128. last_played, _ = strconv.ParseInt(last, 10, 64)
  129. days := pc.DB.GetSetting("days_played", "0")
  130. pc.Days_played, _ = strconv.Atoi(days)
  131. if last_played != pc.Play_day_t {
  132. pc.DB.SetSetting("last_played", strconv.FormatInt(pc.Play_day_t, 10))
  133. pc.DB.SetSetting("days_played", "0")
  134. pc.Days_played = 0
  135. }
  136. pc.Seeds = make([]int32, 0)
  137. // config _seed
  138. parts := strings.Split((*pc.Config)["_seed"], ",")
  139. for _, seed := range parts {
  140. i, _ := strconv.ParseInt(seed, 10, 32)
  141. pc.Seeds = append(pc.Seeds, int32(i))
  142. }
  143. pc.Hand = 0
  144. pc.Total_hands = 0
  145. // spaceAceTriPeaks = make_tripeaks();
  146. {
  147. tripeakstext := " " + SPACEACE + " - Tri-Peaks Solitaire v" + SPACEACE_VERSION + " "
  148. pc.SpaceAceTriPeaks = door.Panel{Width: len(tripeakstext),
  149. Style: door.SINGLE,
  150. BorderColor: door.ColorText("CYAN ON BLACK"),
  151. }
  152. pc.SpaceAceTriPeaks.Lines = append(pc.SpaceAceTriPeaks.Lines,
  153. door.Line{Text: tripeakstext})
  154. }
  155. svRender := RenderStatusValue(door.ColorText("BOLD WHITE ON BLUE"),
  156. door.ColorText("BOLD YELLOW ON BLUE"))
  157. // score_panel = make_score_panel();
  158. {
  159. W := 25
  160. pc.ScorePanel = door.Panel{Width: W}
  161. text := "Name: " + pc.Door.Config.Real_name
  162. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  163. door.Line{Text: fmt.Sprintf("%*s", -W, text), RenderF: svRender})
  164. scoreUpdate := func() string {
  165. txt := fmt.Sprintf("Score: %d", pc.Score)
  166. txt += strings.Repeat(" ", W-len(txt))
  167. return txt
  168. }
  169. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  170. door.Line{Text: scoreUpdate(),
  171. UpdateF: scoreUpdate,
  172. RenderF: svRender,
  173. })
  174. timeUpdate := func() string {
  175. var left int = int(pc.Door.TimeLeft().Minutes())
  176. var used int = int(pc.Door.TimeUsed().Minutes())
  177. txt := fmt.Sprintf("Time used: %3d / %3d", used, left)
  178. txt += strings.Repeat(" ", W-len(txt))
  179. return txt
  180. }
  181. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  182. door.Line{Text: timeUpdate(),
  183. UpdateF: timeUpdate,
  184. RenderF: svRender,
  185. })
  186. handUpdate := func() string {
  187. txt := fmt.Sprintf("Playing Hand %d of %d", pc.Hand, pc.Total_hands)
  188. txt += strings.Repeat(" ", W-len(txt))
  189. return txt
  190. }
  191. pc.ScorePanel.Lines = append(pc.ScorePanel.Lines,
  192. door.Line{Text: handUpdate(),
  193. UpdateF: handUpdate,
  194. RenderF: svRender,
  195. })
  196. }
  197. // streak_panel = make_streak_panel();
  198. {
  199. W := 20
  200. pc.StreakPanel = door.Panel{Width: W}
  201. dateUpdate := func() string {
  202. format, ok := (*pc.Config)["date_format"]
  203. if !ok {
  204. format = "January 2"
  205. }
  206. txt := fmt.Sprintf("Playing: %s", pc.Play_day.Format(format))
  207. txt += strings.Repeat(" ", W-len(txt))
  208. return txt
  209. }
  210. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  211. door.Line{Text: dateUpdate(),
  212. UpdateF: dateUpdate,
  213. RenderF: svRender,
  214. })
  215. currentUpdate := func() string {
  216. txt := fmt.Sprintf("Current Streak: %d", pc.Current_streak)
  217. txt += strings.Repeat(" ", W-len(txt))
  218. return txt
  219. }
  220. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  221. door.Line{Text: currentUpdate(),
  222. UpdateF: currentUpdate,
  223. RenderF: svRender,
  224. })
  225. longestUpdate := func() string {
  226. txt := fmt.Sprintf("Longest Streak: %d", pc.Best_streak)
  227. txt += strings.Repeat(" ", W-len(txt))
  228. return txt
  229. }
  230. pc.StreakPanel.Lines = append(pc.StreakPanel.Lines,
  231. door.Line{Text: longestUpdate(),
  232. UpdateF: longestUpdate,
  233. RenderF: svRender,
  234. })
  235. }
  236. // left_panel = make_left_panel();
  237. {
  238. W := 13
  239. pc.LeftPanel = door.Panel{Width: W}
  240. leftUpdate := func() string {
  241. txt := fmt.Sprintf("Cards left:%d", 51-pc.Play_card)
  242. txt += strings.Repeat(" ", W-len(txt))
  243. return txt
  244. }
  245. pc.LeftPanel.Lines = append(pc.LeftPanel.Lines,
  246. door.Line{Text: leftUpdate(),
  247. UpdateF: leftUpdate,
  248. RenderF: svRender,
  249. })
  250. }
  251. // cmd_panel = make_command_panel();
  252. {
  253. W := 76
  254. pc.CmdPanel = door.Panel{Width: W}
  255. var commands string
  256. if door.Unicode {
  257. commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] Draw [Q]uit [R]edraw [H]elp"
  258. commands += strings.Repeat(" ", W-len([]rune(commands)))
  259. } else {
  260. commands = "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit [R]edraw [H]elp"
  261. commands += strings.Repeat(" ", W-len(commands))
  262. }
  263. cmdRender := cmdLineRender(door.ColorText("BOLD YELLOW ON BLUE"),
  264. door.ColorText("BOLD CYAN ON BLUE"),
  265. door.ColorText("BOLD GREEN ON BLUE"))
  266. pc.CmdPanel.Lines = append(pc.CmdPanel.Lines,
  267. door.Line{Text: commands,
  268. RenderF: cmdRender})
  269. }
  270. // next_quit_panel = make_next_panel();
  271. {
  272. W := 50
  273. pc.NextQuitPanel = door.Panel{Width: W,
  274. Style: door.DOUBLE,
  275. BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
  276. }
  277. nextUpdate := func() string {
  278. var text string
  279. if pc.Select_card != -1 {
  280. text = "[C]ontinue this hand"
  281. }
  282. if pc.Hand < pc.Total_hands {
  283. text += " [N]ext Hand [Q]uit"
  284. } else {
  285. text += " [D]one [Q]uit"
  286. }
  287. text = " " + text + " "
  288. text += strings.Repeat(" ", W-len(text))
  289. return text
  290. }
  291. pc.NextQuitPanel.Lines = append(pc.NextQuitPanel.Lines,
  292. door.Line{Text: nextUpdate(),
  293. UpdateF: nextUpdate,
  294. RenderF: cmdLineRender(door.ColorText("BOLD YEL ON BLU"),
  295. door.ColorText("BOLD CYAN ON BLU"),
  296. door.ColorText("BOLD GREE ON BLUE"))})
  297. }
  298. // calendar = make_calendar();
  299. {
  300. pc.Calendar = door.Screen{}
  301. var month time.Time = time.Now()
  302. FirstOfMonthDate(&month)
  303. }
  304. /*
  305. // This is make_calendar_panel (not make_calendar)
  306. {
  307. W := 41
  308. pc.CalendarPanel = door.Panel{Width: W,
  309. Style: door.DOUBLE,
  310. BorderColor: door.ColorText("CYAN ON BLACK")}
  311. calendarRender := func(text string) string {
  312. // var result string
  313. // var lastColor string
  314. result := door.Render{Line: text}
  315. digits := door.ColorText("CYAN ON BLACK")
  316. digits_play := door.ColorText("BOLD CYAN ON BLACK")
  317. spaces := door.ColorText("WHITE ON BLACK")
  318. open := door.ColorText("BOLD GREEN ON BLACK")
  319. hands := door.ColorText("BOLD YELLOW ON BLACK")
  320. full := door.ColorText("RED ON BLACK")
  321. nny := door.ColorText("BOLD BLACK ON BLACK")
  322. for days := 0; days < 7; days++ {
  323. var dayText string
  324. if days == 0 {
  325. result.Append(spaces, 1)
  326. }
  327. dayText = text[1+days*6 : (1+days*6)+3]
  328. if dayText[1] == ' ' {
  329. result.Append(spaces, 3)
  330. } else {
  331. // Something is here
  332. cday := dayText[2]
  333. if cday == 'o' || cday == 'h' {
  334. result.Append(digits_play, 2)
  335. } else {
  336. result.Append(digits, 2)
  337. }
  338. switch cday {
  339. case 'o':
  340. result.Append(open, 1)
  341. case 'h':
  342. result.Append(hands, 1)
  343. case 'x':
  344. result.Append(full, 1)
  345. case 'u':
  346. result.Append(nny, 1)
  347. }
  348. }
  349. if days == 6 {
  350. result.Append(spaces, 1)
  351. } else {
  352. result.Append(spaces, 3)
  353. }
  354. }
  355. return result.Result
  356. }
  357. for row := 0; row < 6; row++ {
  358. calendarUpdate := func() string {
  359. var text string
  360. for d := 0; d < 7; d++ {
  361. text += " "
  362. v := pc.Day_status[(row*7)+d]
  363. if v == 0 {
  364. text += " "
  365. } else {
  366. text += fmt.Sprintf("%-2d", v)
  367. status := pc.Day_status[v-1]
  368. switch status {
  369. case 0:
  370. text += "o"
  371. case 1:
  372. text += "h"
  373. case 2:
  374. text += "x"
  375. case 3:
  376. text += "u"
  377. }
  378. }
  379. if d == 6 {
  380. text += " "
  381. } else {
  382. text += " "
  383. }
  384. }
  385. return text
  386. }
  387. pc.Calendar.Lines = append(pc.Calendar.Lines,
  388. door.Line{Text: calendarUpdate(),
  389. UpdateF: calendarUpdate,
  390. RenderF: calendarRender})
  391. }
  392. }
  393. */
  394. }
  395. func (pc *PlayCards) Play() {
  396. // this defaults (for now) to playing today (if unplayed).
  397. // Otherwise display calendar.
  398. pc.Play_day = time.Now()
  399. NormalizeDate(&pc.Play_day)
  400. pc.Play_day_t = pc.Play_day.Unix()
  401. played := pc.DB.HandsPlayedOnDay(pc.Play_day_t)
  402. var r int
  403. if played == 0 {
  404. pc.Door.Write("Let's play today..." + door.CRNL)
  405. time.Sleep(time.Second)
  406. pc.Hand = 1
  407. r = pc.PlayCards()
  408. if r != 'D' {
  409. return
  410. }
  411. } else {
  412. if played < pc.Total_hands {
  413. // let's finish today...
  414. pc.Hand = played + 1
  415. r = pc.PlayCards()
  416. if r != 'D' {
  417. return
  418. }
  419. }
  420. }
  421. /*
  422. CALENDAR_UPDATE:
  423. pc.UpdateCalendarDays()
  424. pc.Calendar.Update()
  425. pc.Door.Write(pc.Calendar.Output())
  426. */
  427. }
  428. func int32toByteArray(i int32) (arr [4]byte) {
  429. binary.BigEndian.PutUint32(arr[0:4], uint32(i))
  430. return
  431. }
  432. func int64toByteArray(i int64) (arr [8]byte) {
  433. binary.BigEndian.PutUint64(arr[0:8], uint64(i))
  434. return
  435. }
  436. func (pc *PlayCards) PlayCards() int {
  437. pc.InitValues()
  438. var game_width int
  439. var game_height int = 20
  440. pos := CardPos[27]
  441. game_width = pos.X + 5
  442. MX := door.Width
  443. MY := door.Height
  444. pc.Off_X = (MX - game_width) / 2
  445. pc.Off_Y = (MY - game_height) / 2
  446. // _ = off_x
  447. // We can now position things properly centered
  448. pc.SpaceAceTriPeaks.X = (MX - pc.SpaceAceTriPeaks.Width) / 2
  449. pc.SpaceAceTriPeaks.Y = pc.Off_Y
  450. pc.Off_Y += 3
  451. currentDefault := pc.DB.GetSetting("DeckColor", "ALL")
  452. Next_Hand:
  453. deck_color := StringToANSIColor(currentDefault)
  454. pc.DeckPanel.SetBackColor(deck_color)
  455. pc.Play_card = 28
  456. pc.Select_card = 23
  457. pc.Score = 0
  458. pc.Current_streak = 0
  459. // Use play day to seed RNG
  460. {
  461. // Secret Squirrel method of seeding the RNG
  462. seed_seq := make([]byte, 0)
  463. for _, seed := range pc.Seeds {
  464. ba := int32toByteArray(seed)
  465. seed_seq = append(seed_seq, ba[:]...)
  466. // sha1.Sum(ba[:])
  467. }
  468. pd := int64toByteArray(pc.Play_day_t)
  469. seed_seq = append(seed_seq, pd[:]...)
  470. // We also need the hand # that we're playing.
  471. seed_seq = append(seed_seq, byte(pc.Hand))
  472. result := sha1.Sum(seed_seq)
  473. var seed int64 = int64(binary.BigEndian.Uint64(result[0:8]))
  474. pc.RNG.Seed(seed)
  475. // I'm seeing changes in the seed_seq bytes, but the seed is the same number.
  476. // log.Printf("%#v\nSeed %d\nLen %d\nresult %#v\n", seed_seq, seed, len(seed_seq), result)
  477. pc.Deck = ShuffleCards(pc.RNG, 1)
  478. pc.State = MakeCardStates(1)
  479. }
  480. // Position the panels
  481. {
  482. off_yp := pc.Off_Y + 11
  483. left_panel_x := CardPos[18].X
  484. right_panel_x := CardPos[15].X
  485. pc.ScorePanel.X = left_panel_x + pc.Off_X
  486. pc.ScorePanel.Y = off_yp
  487. pc.StreakPanel.X = right_panel_x + pc.Off_X
  488. pc.StreakPanel.Y = off_yp
  489. pc.CmdPanel.X = left_panel_x + pc.Off_X
  490. pc.CmdPanel.Y = off_yp + 5
  491. next_off_x := (MX - pc.NextQuitPanel.Width) / 2
  492. pc.NextQuitPanel.X = next_off_x
  493. pc.NextQuitPanel.Y = CardPos[10].Y + pc.Off_Y
  494. }
  495. var Dealing bool = true
  496. var r int = 0
  497. pc.Redraw(Dealing)
  498. Dealing = false
  499. pc.LeftPanel.Update()
  500. pc.Door.Write(door.Reset)
  501. var c *door.Panel
  502. var in_game bool = true
  503. var save_streak bool = false
  504. var waiting int = 0
  505. for in_game {
  506. var output string
  507. output = pc.ScorePanel.Update()
  508. if output != "" {
  509. pc.Door.Write(output)
  510. {
  511. // Redisplay Mark / Selected Card
  512. Pos := &CardPos[pc.Select_card]
  513. c = &pc.DeckPanel.Mark[1]
  514. c.X = Pos.X + pc.Off_X + 2
  515. c.Y = Pos.Y + pc.Off_Y + 2
  516. pc.Door.Write(c.Output())
  517. }
  518. }
  519. if save_streak {
  520. save_streak = false
  521. pc.DB.SetSetting("best_streak", strconv.Itoa(pc.Best_streak))
  522. }
  523. waiting = 0
  524. for waiting < int(door.Inactivity) {
  525. r = pc.Door.WaitKey(1, 0)
  526. if r == -1 {
  527. // TIMEOUT is expected here
  528. waiting++
  529. output = pc.ScorePanel.Update()
  530. if output != "" {
  531. pc.Door.Write(output)
  532. {
  533. // Redisplay Mark / Selected Card
  534. Pos := &CardPos[pc.Select_card]
  535. c = &pc.DeckPanel.Mark[1]
  536. c.X = Pos.X + pc.Off_X + 2
  537. c.Y = Pos.Y + pc.Off_Y + 2
  538. pc.Door.Write(c.Output())
  539. }
  540. }
  541. } else {
  542. break
  543. }
  544. }
  545. if r > 0 {
  546. // Not a timeout
  547. /*
  548. if r < 0x1000 {
  549. // not a function key
  550. r = int(unicode.ToUpper(rune(r)))
  551. }
  552. */
  553. switch r {
  554. case '\x0d':
  555. // Next Card
  556. if pc.Current_streak > pc.Best_streak {
  557. pc.Best_streak = pc.Current_streak
  558. save_streak = true
  559. pc.Door.Write(pc.StreakPanel.Update())
  560. }
  561. if pc.Play_card < 51 {
  562. pc.Play_card++
  563. pc.Current_streak = 0
  564. pc.Door.Write(pc.StreakPanel.Update())
  565. pc.Door.Write(pc.LeftPanel.Update())
  566. // Deal the next card
  567. if pc.Play_card == 51 {
  568. // out of cards
  569. cpos := &CardPos[29]
  570. c = &pc.DeckPanel.Backs[0]
  571. c.X = cpos.X + pc.Off_X
  572. c.Y = cpos.Y + pc.Off_Y
  573. pc.Door.Write(c.Output())
  574. }
  575. cpos := &CardPos[28]
  576. c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]]
  577. c.X = cpos.X + pc.Off_X
  578. c.Y = cpos.Y + pc.Off_Y
  579. pc.Door.Write(c.Output())
  580. }
  581. case 'R', 'r':
  582. pc.Redraw(false)
  583. case 'Q', 'q':
  584. // Possibly prompt here for [N]ext hand or [Q]uit
  585. if pc.Current_streak > pc.Best_streak {
  586. pc.Best_streak = pc.Current_streak
  587. pc.Door.Write(pc.StreakPanel.Update())
  588. }
  589. pc.NextQuitPanel.Update()
  590. pc.Door.Write(pc.NextQuitPanel.Output())
  591. if pc.State[26] == 2 {
  592. pc.Door.Write(door.ColorText("BLACK"))
  593. } else {
  594. pc.Door.Write(door.Reset)
  595. }
  596. if pc.Hand < pc.Total_hands {
  597. r = pc.Door.GetOneOf("CNQ")
  598. } else {
  599. r = pc.Door.GetOneOf("CDQ")
  600. }
  601. if r == 'C' {
  602. // Continue
  603. pc.Redraw(false)
  604. } else {
  605. if r == 'D' || r == 'Q' {
  606. if pc.Score >= 50 {
  607. pc.DB.SaveScore(time.Now().Unix(), pc.Play_day_t, pc.Hand, 0, pc.Score)
  608. }
  609. in_game = false
  610. } else {
  611. if r == 'N' {
  612. pc.DB.SaveScore(time.Now().Unix(), pc.Play_day_t, pc.Hand, 0, pc.Score)
  613. pc.Hand++
  614. goto Next_Hand
  615. }
  616. }
  617. }
  618. case ' ', '5':
  619. if CanPlay(pc.Deck[pc.Select_card],
  620. pc.Deck[pc.Play_card]) {
  621. pc.Current_streak++
  622. pc.Door.Write(pc.StreakPanel.Update())
  623. pc.Score += 10
  624. if pc.Current_streak > 1 {
  625. pc.Score += pc.Current_streak * 5
  626. }
  627. pc.Door.Write(pc.ScorePanel.Update())
  628. // Play card
  629. pc.State[pc.Select_card] = 2
  630. {
  631. // Swap out the select card with the play card
  632. temp := pc.Deck[pc.Select_card]
  633. pc.Deck[pc.Select_card] = pc.Deck[pc.Play_card]
  634. pc.Deck[pc.Play_card] = temp
  635. // Select card is invalidated here. Find new card.
  636. check := Unblocks(pc.Select_card)
  637. var left bool = false
  638. var right bool = false
  639. for _, chk := range check {
  640. blk := Blocks[chk]
  641. if blk[0] == pc.Select_card {
  642. right = true
  643. }
  644. if blk[1] == pc.Select_card {
  645. left = true
  646. }
  647. }
  648. cardback_color := pc.DeckPanel.Backs[1].Lines[0].DefaultColor
  649. pc.Door.Write(RemoveCard(pc.Select_card, cardback_color, pc.Off_X, pc.Off_Y, left, right))
  650. // Redraw play card #28 ("old" select_card)
  651. cpos := &CardPos[28]
  652. c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]]
  653. c.X = cpos.X + pc.Off_X
  654. c.Y = cpos.Y + pc.Off_Y
  655. pc.Door.Write(c.Output())
  656. // Did we unhide a card?
  657. var new_card_shown int = -1
  658. if len(check) > 0 {
  659. for _, chk := range check {
  660. blk := Blocks[chk]
  661. if pc.State[blk[0]] == 2 &&
  662. pc.State[blk[1]] == 2 {
  663. pc.State[chk] = 1
  664. cpos := &CardPos[chk]
  665. c = &pc.DeckPanel.Cards[pc.Deck[chk]]
  666. c.X = cpos.X + pc.Off_X
  667. c.Y = cpos.Y + pc.Off_Y
  668. pc.Door.Write(c.Output())
  669. new_card_shown = chk
  670. }
  671. }
  672. } else {
  673. // top card cleared
  674. cpos := &CardPos[pc.Select_card]
  675. pc.Door.Write(door.Goto(cpos.X+pc.Off_X, cpos.Y+pc.Off_Y) + Bonus())
  676. pc.Score += 100
  677. pc.State[pc.Select_card] = 3 // Handle in "redraw"
  678. pc.Door.Write(pc.ScorePanel.Update())
  679. }
  680. // Find new number for select_card.
  681. if new_card_shown != -1 {
  682. pc.Select_card = new_card_shown
  683. } else {
  684. new_select := FindClosestActiveCard(&pc.State, pc.Select_card)
  685. if new_select != -1 {
  686. pc.Select_card = new_select
  687. } else {
  688. log.Println("Winner")
  689. pc.Select_card = -1
  690. pc.Score += 15 * (51 - pc.Play_card)
  691. pc.Door.Write(pc.ScorePanel.Update())
  692. // Save Score
  693. pc.DB.SaveScore(time.Now().Unix(), pc.Play_day_t, pc.Hand, 1, pc.Score)
  694. pc.NextQuitPanel.Update()
  695. pc.Door.Write(pc.NextQuitPanel.Output())
  696. if pc.State[26] == 2 {
  697. pc.Door.Write(door.ColorText("BLACK"))
  698. } else {
  699. pc.Door.Write(door.Reset)
  700. }
  701. if pc.Hand < pc.Total_hands {
  702. r = pc.Door.GetOneOf("NQ")
  703. } else {
  704. r = pc.Door.GetOneOf("DQ")
  705. }
  706. if r == 'N' {
  707. pc.Hand++
  708. goto Next_Hand
  709. }
  710. in_game = false
  711. // if r == 'D' ?
  712. if r == 'Q' {
  713. // this seemed odd. We quit the game ?
  714. r = 'Q'
  715. }
  716. }
  717. }
  718. // Update the select_card marker
  719. Pos := &CardPos[pc.Select_card]
  720. c = &pc.DeckPanel.Mark[1]
  721. c.X = Pos.X + pc.Off_X + 2
  722. c.Y = Pos.Y + pc.Off_Y + 2
  723. pc.Door.Write(c.Output() + door.Reset)
  724. }
  725. }
  726. case door.XKEY_LEFT_ARROW, '4':
  727. var new_select int = FindNextActiveCard(true, &pc.State, pc.Select_card)
  728. if new_select >= 0 {
  729. Pos := &CardPos[pc.Select_card]
  730. c = &pc.DeckPanel.Mark[0]
  731. c.X = Pos.X + pc.Off_X + 2
  732. c.Y = Pos.Y + pc.Off_Y + 2
  733. pc.Door.Write(c.Output())
  734. pc.Select_card = new_select
  735. Pos = &CardPos[pc.Select_card]
  736. c = &pc.DeckPanel.Mark[1]
  737. c.X = Pos.X + pc.Off_X + 2
  738. c.Y = Pos.Y + pc.Off_Y + 2
  739. pc.Door.Write(c.Output() + door.Reset)
  740. }
  741. case door.XKEY_RIGHT_ARROW, '6':
  742. var new_select int = FindNextActiveCard(false, &pc.State, pc.Select_card)
  743. if new_select >= 0 {
  744. Pos := &CardPos[pc.Select_card]
  745. c = &pc.DeckPanel.Mark[0]
  746. c.X = Pos.X + pc.Off_X + 2
  747. c.Y = Pos.Y + pc.Off_Y + 2
  748. pc.Door.Write(c.Output())
  749. pc.Select_card = new_select
  750. Pos = &CardPos[pc.Select_card]
  751. c = &pc.DeckPanel.Mark[1]
  752. c.X = Pos.X + pc.Off_X + 2
  753. c.Y = Pos.Y + pc.Off_Y + 2
  754. pc.Door.Write(c.Output() + door.Reset)
  755. }
  756. }
  757. } else {
  758. in_game = false
  759. }
  760. }
  761. return r
  762. }
  763. func (pc *PlayCards) Redraw(Dealing bool) {
  764. // stars.display()
  765. pc.Door.Write(door.Clrscr + door.Reset)
  766. pc.Door.Write(pc.SpaceAceTriPeaks.Output())
  767. var c *door.Panel
  768. {
  769. // Step 1: Draw the deck "source"
  770. pos := CardPos[29]
  771. if pc.Play_card == 51 {
  772. // out of cards
  773. pos.Level = 0
  774. }
  775. c = &pc.DeckPanel.Backs[pos.Level]
  776. c.X = pos.X + pc.Off_X
  777. c.Y = pos.Y + pc.Off_Y
  778. pc.LeftPanel.X = pos.X + pc.Off_X
  779. pc.LeftPanel.Y = pos.Y + pc.Off_Y + 3 // const height see deck
  780. pc.ScorePanel.Update()
  781. pc.LeftPanel.Update()
  782. pc.StreakPanel.Update()
  783. pc.CmdPanel.Update()
  784. pc.Door.Write(pc.ScorePanel.Output())
  785. pc.Door.Write(pc.LeftPanel.Output())
  786. pc.Door.Write(pc.StreakPanel.Output())
  787. pc.Door.Write(pc.CmdPanel.Output())
  788. pc.Door.Write(c.Output())
  789. if Dealing {
  790. time.Sleep(time.Second)
  791. }
  792. }
  793. var x_max int = 29
  794. if Dealing {
  795. x_max = 28
  796. }
  797. for x := 0; x < x_max; x++ {
  798. pos := CardPos[x]
  799. if Dealing {
  800. time.Sleep(time.Duration(75) * time.Millisecond)
  801. }
  802. if Dealing {
  803. c = &pc.DeckPanel.Backs[pos.Level]
  804. c.X = pos.X + pc.Off_X
  805. c.Y = pos.Y + pc.Off_Y
  806. pc.Door.Write(c.Output())
  807. } else {
  808. switch pc.State[x] {
  809. case 0:
  810. c = &pc.DeckPanel.Backs[pos.Level]
  811. c.X = pos.X + pc.Off_X
  812. c.Y = pos.Y + pc.Off_Y
  813. pc.Door.Write(c.Output())
  814. case 1:
  815. if x == 28 {
  816. c = &pc.DeckPanel.Cards[pc.Deck[pc.Play_card]]
  817. } else {
  818. c = &pc.DeckPanel.Cards[pc.Deck[x]]
  819. }
  820. c.X = pos.X + pc.Off_X
  821. c.Y = pos.Y + pc.Off_Y
  822. pc.Door.Write(c.Output())
  823. case 2:
  824. // no card to draw.
  825. case 3:
  826. // peak cleared, draw bonus
  827. var output string = door.Goto(pos.X+pc.Off_X, pos.Y+pc.Off_Y)
  828. output += Bonus()
  829. pc.Door.Write(output)
  830. }
  831. }
  832. }
  833. if Dealing {
  834. for x := 18; x < 29; x++ {
  835. pc.State[x] = 1
  836. // CardPos[x] X Y Level
  837. Pos := &CardPos[x]
  838. time.Sleep(time.Duration(200) * time.Millisecond)
  839. c = &pc.DeckPanel.Cards[pc.Deck[x]]
  840. c.X = Pos.X + pc.Off_X
  841. c.Y = Pos.Y + pc.Off_Y
  842. pc.Door.Write(c.Output())
  843. }
  844. }
  845. {
  846. Pos := &CardPos[pc.Select_card]
  847. c = &pc.DeckPanel.Mark[1]
  848. c.X = Pos.X + pc.Off_X + 2
  849. c.Y = Pos.Y + pc.Off_Y + 2
  850. pc.Door.Write(c.Output())
  851. }
  852. }
  853. func Bonus() string {
  854. return door.ColorText("BOLD YELLOW") + "BONUS"
  855. }
  856. func (pc *PlayCards) UpdateCalendarDays() {
  857. }