playcards.go 22 KB

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