package main import ( "math/rand" "red-green/door" "strings" ) type Deck struct { Cards []door.Panel Backs []door.Panel Mark []door.Panel } func (d *Deck) SetBackColor(color string) { for x := 0; x < 5; x++ { for idx := range d.Backs[x].Lines { d.Backs[x].Lines[idx].DefaultColor = color } } } func (d *Deck) Init() { d.Cards = make([]door.Panel, 52) for x := 0; x < 52; x++ { d.Cards[x] = CardOf(x) } d.Backs = make([]door.Panel, 5) for x := 0; x < 5; x++ { d.Backs[x] = BackOf(x) } d.Mark = make([]door.Panel, 2) d.Mark[0] = MarkOf(0) d.Mark[1] = MarkOf(1) } func CardOf(c int) door.Panel { suit := GetSuit(c) rank := GetRank(c) var is_red bool = suit < 2 var color string if is_red { color = door.ColorText("RED ON WHITE") } else { color = door.ColorText("BLACK ON WHITE") } p := door.Panel{ X: 0, Y: 0, Width: 5} r := RankSymbol(rank) s := SuitSymbol(suit) p.Lines = append(p.Lines, door.Line{Text: r + s + " ", DefaultColor: color}) p.Lines = append(p.Lines, door.Line{Text: " " + s + " ", DefaultColor: color}) p.Lines = append(p.Lines, door.Line{Text: " " + s + r, DefaultColor: color}) return p } func BackOf(level int) door.Panel { p := door.Panel{ X: 0, Y: 0, Width: 5, } var back string = strings.Repeat(BackSymbol(level), 5) for x := 0; x < 3; x++ { p.Lines = append(p.Lines, door.Line{Text: back}) } return p } func MarkOf(c int) door.Panel { p := door.Panel{Width: 1} color := door.ColorText("BLUE ON WHITE") // var m rune if c == 0 { p.Lines = append(p.Lines, door.Line{Text: " ", DefaultColor: color, }) } else { if door.Unicode { p.Lines = append(p.Lines, door.Line{Text: "\u25a0", DefaultColor: color, }) // m = '\u25a0' } else { // Safely convert from byte to string var b [1]byte b[0] = 0xfe p.Lines = append(p.Lines, door.Line{Text: string(b[:]), DefaultColor: color}) } } return p } func RankSymbol(c int) string { const symbols = "A23456789TJQK" return symbols[c : c+1] } func SuitSymbol(c int) string { if door.Unicode { switch c { case 0: return "\u2665" case 1: return "\u2666" case 2: return "\u2663" case 3: return "\u2660" } } else { if door.Full_CP437 { switch c { case 0: return "\x03" case 1: return "\x04" case 2: return "\x05" case 3: return "\x06" } } else { switch c { case 0: return "*" case 1: return "^" case 2: return "%" case 3: return "$" } } } return "?" } func BackSymbol(level int) string { if level == 0 { return " " } if door.Unicode { switch level { case 1: return "\u2591" case 2: return "\u2592" case 3: return "\u2593" case 4: return "\u2588" } } else { switch level { case 1: return "\xb0" case 2: return "\xb1" case 3: return "\xb2" case 4: return "\xdb" } } return "?" } func GetRank(c int) int { return (c % 52) % 13 } func GetSuit(c int) int { return (c % 52) / 13 } func GetDeck(c int) int { return c / 52 } type Pos struct { X int Y int Level int } func CalcCardPos(pos int) Pos { var result Pos const space = 3 const height = 3 if pos == 28 { result = CalcCardPos(23) result.Y += height + 1 result.Level-- return result } else { if pos == 29 { result = CalcCardPos(22) result.Y += height + 1 result.Level-- return result } } const CARD_WIDTH = 5 var HALF_WIDTH int = 3 HALF_WIDTH += space / 2 const between = CARD_WIDTH + space if pos < 3 { result.Level = 1 result.Y = (result.Level-1)*(height-1) + 1 result.X = pos*(between*3) + between + HALF_WIDTH + space return result } else { pos -= 3 } if pos < 6 { result.Level = 2 result.Y = (result.Level-1)*(height-1) + 1 group := pos / 2 result.X = pos*between + (group * between) + CARD_WIDTH + space*2 return result } else { pos -= 6 } if pos < 9 { result.Level = 3 result.Y = (result.Level-1)*(height-1) + 1 result.X = pos*between + HALF_WIDTH + space return result } else { pos -= 9 } if pos < 10 { result.Level = 4 result.Y = (result.Level-1)*(height-1) + 1 result.X = pos*between + space return result } // failure result.Level = -1 result.X = -1 result.Y = -1 return result } // 0-29 var CardPos []Pos // What cards block which card? var Blocks [][]int /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 */ // Pre-calculate the card positions func init() { CardPos = make([]Pos, 30) for x := 0; x < 30; x++ { CardPos[x] = CalcCardPos(x) } Blocks = [][]int{ {3, 4}, {5, 6}, {7, 8}, // end row 1 {9, 10}, {10, 11}, {12, 13}, {13, 14}, {15, 16}, {16, 17}, // end row 2 {18, 19}, {19, 20}, {20, 21}, {21, 22}, {22, 23}, {23, 24}, {24, 25}, {25, 26}, {26, 27}, } } // Which card(s) are unblocked by this card? func Unblocks(card int) []int { var result []int for idx := range Blocks { if Blocks[idx][0] == card || Blocks[idx][1] == card { result = append(result, idx) } } return result } func CanPlay(card1 DeckType, card2 DeckType) bool { rank1 := GetRank(int(card1)) rank2 := GetRank(int(card2)) if (rank1+1)%13 == rank2 { return true } if rank1 == 0 { rank1 += 13 } if rank1-1 == rank2 { return true } return false } // Set the size used to represent cards in the deck. // 1 deck = 52, 2 decks = 104, 4 decks = 208 // If we need more then 4 decks, we'll need a bigger // type here. type DeckType int8 /* This shuffles the deck(s) of cards. The RNG must be seeded before calling. */ func ShuffleCards(RNG *rand.Rand, decks int) []DeckType { var size int = decks * 52 var result []DeckType = make([]DeckType, size) for x := 0; x < size; x++ { result[x] = DeckType(x) } RNG.Shuffle(size, func(i, j int) { result[i], result[j] = result[j], result[i] }) return result } // Create an array to hold the deck state. // Has it played? Has it been unblocked/face up? func MakeCardStates(decks int) []DeckType { var size int = decks * 52 var result []DeckType = make([]DeckType, size) // defaults to 0 return result } func RemoveCard(c int, back_color string, off_x int, off_y int, left bool, right bool) string { var result string // We are modifying this -- make copy Pos := CardPos[c] if Pos.Level > 1 { Pos.Level-- } cstr := BackSymbol(Pos.Level) result = door.Goto(Pos.X+off_x, Pos.Y+off_y) + back_color if left { result += cstr } else { result += " " } result += " " if right { result += cstr } else { result += " " } result += door.Goto(Pos.X+off_x, Pos.Y+off_y+1) + " " result += door.Goto(Pos.X+off_x, Pos.Y+off_y+2) + " " return result } func FindNextActiveCard(left bool, state *[]DeckType, current int) int { Pos := &CardPos[current] var current_x int = Pos.X var pos int = -1 var pos_x int var max_pos int = -1 var max_x int = -1 var min_pos int = -1 var min_x int = 100 if left { pos_x = 0 } else { pos_x = 100 } for x := 0; x < 28; x++ { if (*state)[x] == 1 { // Possible location if x == current { continue } Pos = &CardPos[x] // find max and min while we're iterating here if Pos.X < min_x { min_pos = x min_x = Pos.X } if Pos.X > max_x { max_pos = x max_x = Pos.X } if left { if Pos.X < current_x && Pos.X > pos_x { pos_x = Pos.X pos = x } } else { if Pos.X > current_x && Pos.X < pos_x { pos_x = Pos.X pos = x } } } } if pos == -1 { // we couldn't find one if left { // use max -- roll around to the right pos = max_pos } else { // use min -- roll around to the left pos = min_pos } } return pos } func FindClosestActiveCard(state *[]DeckType, current int) int { Pos := &CardPos[current] var current_x int = Pos.X var pos int = -1 var pos_x int = -1 for x := 0; x < 28; x++ { if (*state)[x] == 1 { // Possible location if x == current { continue } xPos := &CardPos[x] if pos == -1 { pos = x pos_x = xPos.X } else { if Abs(current_x-xPos.X) < Abs(current_x-pos_x) { pos = x pos_x = xPos.X } } } } return pos }