Skip to content

Instantly share code, notes, and snippets.

@thykka
Last active November 19, 2020 02:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thykka/78986cff00e013fcae4773e157da09aa to your computer and use it in GitHub Desktop.
Save thykka/78986cff00e013fcae4773e157da09aa to your computer and use it in GitHub Desktop.
SCRIPT-8
// title: Cardette
const SUIT_HEARTS = 0
const SUIT_DIAMONDS = 1
const SUIT_SPADES = 2
const SUIT_CLUBS = 3
const SUITS = [SUIT_HEARTS, SUIT_DIAMONDS, SUIT_SPADES, SUIT_CLUBS]
const SHADES = [3, 3, 6, 6]
const VALUES = range(1, 7)
const SPRITES = {
corner_visible: 0,
edge_visible: 1,
corner_hidden: 2,
edge_hidden: 3,
[SUIT_HEARTS]: 4,
[SUIT_DIAMONDS]: 5,
[SUIT_SPADES]: 6,
[SUIT_CLUBS]: 7
}
const SPRITE_CURSOR = 8
const BETS = [
{ suits: [SUIT_HEARTS, SUIT_DIAMONDS], multiplier: 2 },
{ suits: [SUIT_SPADES, SUIT_CLUBS], multiplier: 2 },
{ values: [1, 2], multiplier: 3 },
{ values: [3, 4], multiplier: 3 },
{ values: [5, 6], multiplier: 3 },
{ suits: [SUIT_HEARTS], multiplier: 4 },
{ suits: [SUIT_DIAMONDS], multiplier: 4 },
{ suits: [SUIT_SPADES], multiplier: 4 },
{ suits: [SUIT_CLUBS], multiplier: 4 },
...VALUES.map(value => ({ values: [value], multiplier: 6 })),
...SUITS.flatMap(suit =>
VALUES.map(value => ({ suits: [suit], values: [value], multiplier: 24 }))
)
]
const GRID_W = 16
const GRID_H = 16
const GRID_BETS_START = BETS.length - (SUITS.length * VALUES.length)
const BET_AMOUNTS = [1, 2, 3]
const STATE_BET_AMOUNT = 0
const STATE_BET_TYPE = 1
const STATE_DEAL = 2
const STATE_PICK = 3
const STATE_RESULT = 4
const STATE_CONTINUE = 5
const GAMESTATES = [
STATE_BET_AMOUNT,
STATE_BET_TYPE,
STATE_DEAL,
STATE_PICK,
STATE_RESULT,
STATE_CONTINUE
]
const createDeck = (suits, values) => {
return suits
.flatMap(suit => values.map(value => [suit, value, Math.random()]))
.sort((a, b) => a[2] - b[2]) // Shuffle deck
.map(([suit, value]) => [suit, value])
}
init = state => {
state.deck = createDeck(SUITS, VALUES)
state.currentBet = 0
state.currentBetAmount = 0
state.gameState = 0
state.credits = 10
state.dealt = []
state.currentDealt = 0
}
const modify = (current, list, amount = 1) => {
return (current + list.length + amount) % list.length
}
const updateBetSelection = (state, input) => {
if (input.upPressed) {
state.currentBet = modify(state.currentBet, BETS, -1)
}
if (input.downPressed) {
state.currentBet = modify(state.currentBet, BETS, 1)
}
if (input.leftPressed) {
if (state.currentBet <= GRID_BETS_START) {
// sequential movement
state.currentBet = modify(state.currentBet, BETS, -1)
} else {
if (state.currentBet <= GRID_BETS_START + VALUES.length - 1) {
// wrap to previous row
state.currentBet = modify(
state.currentBet,
BETS,
VALUES.length * (SUITS.length - 1) - 1
)
} else {
// move left
state.currentBet = modify(state.currentBet, BETS, -VALUES.length)
}
}
}
if (input.rightPressed) {
if (state.currentBet >= GRID_BETS_START && state.currentBet !== 38) {
if (
state.currentBet >=
GRID_BETS_START + VALUES.length * (SUITS.length - 1)
) {
// wrap to next row
state.currentBet = modify(
state.currentBet,
BETS,
1 - VALUES.length * (SUITS.length - 1)
)
} else {
// move right
state.currentBet = modify(state.currentBet, BETS, VALUES.length)
}
} else {
// sequential movement
state.currentBet = modify(state.currentBet, BETS, 1)
}
}
if (input.aPressed || input.startPressed) {
nextState(state)
}
}
const nextState = state => {
state.gameState = modify(state.gameState, GAMESTATES, 1)
}
const updateBetAmount = (state, input) => {
const availableBets = BET_AMOUNTS.filter(amount => amount <= state.credits)
if (state.currentBetAmount >= availableBets.length) state.currentBetAmount = 0
if (input.upPressed || input.leftPressed) {
state.currentBetAmount = modify(state.currentBetAmount, availableBets, -1)
}
if (input.downPressed || input.rightPressed) {
state.currentBetAmount = modify(state.currentBetAmount, availableBets, 1)
}
if (input.aPressed || input.startPressed) {
state.credits -= BET_AMOUNTS[state.currentBetAmount]
nextState(state)
}
}
const updateDeal = (state, input) => {
state.dealt = [popFromDeck(state), popFromDeck(state)]
nextState(state)
}
const popFromDeck = state => {
return state.deck.pop()
}
const updatePickCard = (state, input) => {
if (input.upPressed || input.leftPressed) {
state.currentDealt = modify(state.currentDealt, state.dealt, 1)
}
if (input.downPressed || input.rightPressed) {
state.currentDealt = modify(state.currentDealt, state.dealt, -1)
}
if (input.aPressed || input.startPressed) {
nextState(state)
}
}
const updateResult = (state, input) => {
const win = checkWin(state)
if (win) {
state.win =
BET_AMOUNTS[state.currentBetAmount] * BETS[state.currentBet].multiplier
state.credits += state.win
}
nextState(state)
}
const checkWin = state => {
const bet = BETS[state.currentBet]
const card = state.dealt[state.currentDealt]
return betMatches(bet, ...card)
}
const waitForNextRound = (state, input) => {
if (input.aPressed || input.startPressed) {
state.win = 0
if (state.deck.length < 2) {
state.deck = createDeck(SUITS, VALUES)
}
nextState(state)
}
}
update = (state, input, elapsed) => {
switch (state.gameState) {
case STATE_BET_AMOUNT:
updateBetAmount(state, input)
break
case STATE_BET_TYPE:
updateBetSelection(state, input)
break
case STATE_DEAL:
updateDeal(state)
break
case STATE_PICK:
updatePickCard(state, input)
break
case STATE_RESULT:
updateResult(state, input)
break
case STATE_CONTINUE:
log(BETS[state.currentBet])
waitForNextRound(state, input)
break
default:
log('no such state: ' + state.gameState)
}
}
const drawCard = (position, hidden, value, suit) => {
const cornerSprite = hidden ? SPRITES.corner_hidden : SPRITES.corner_visible
const edgeSprite = hidden ? SPRITES.edge_hidden : SPRITES.edge_visible
const [x, y] = position
sprite(x, y, cornerSprite)
sprite(x + 8, y, cornerSprite, 0, true)
sprite(x, y + 8, edgeSprite, 0)
sprite(x + 8, y + 8, edgeSprite, 0, true)
sprite(x, y + 16, cornerSprite, 0, false, true)
sprite(x + 8, y + 16, cornerSprite, 0, true, true)
if (hidden) return
print(x + 3, y + 3, value, SHADES[suit])
sprite(x + 4, y + 11, SPRITES[suit], 0 - SHADES[suit])
}
const drawDeck = ([x, y], deck) => {
Array.from({ length: Math.ceil(deck.length / 6) }).forEach((_, i) => {
drawCard([x + i * 2, y + i * 2], 1, ...deck[i])
})
const offset = Math.ceil(deck.length / 6) * 2
print(x + 16 + offset, y + 17 + offset, deck.length, 3)
print(x + 16 + offset, y + 10 + offset, 'left:', 3)
}
const drawBetGrid = ([x, y] = [0, 0], state) => {
const { currentBet, currentBetAmount } = state
const bet = BETS[currentBet]
state.gameState === STATE_BET_TYPE && print(x + 32, y + 1, `place your bet`)
print(x + 12, y + 8, `pays`)
print(x + 12, y + 14, `${bet.multiplier}x${BET_AMOUNTS[currentBetAmount]}`)
range(0, SUITS.length / 2).forEach(suitIndex => {
const tileY = y + 8
const tileX = 32 + x + suitIndex * GRID_W * 2
const highlight = currentBet === suitIndex
rectFill(tileX, tileY, GRID_W * 2 - 1, 11, highlight ? 0 : 4)
!highlight && rectStroke(tileX, tileY, GRID_W * 2 - 1, 11, 5)
sprite(
tileX + 3,
tileY + 1,
SPRITES[suitIndex * 2],
highlight ? -SHADES[suitIndex * 2] : -5
)
print(tileX + 12, tileY + 3, 'or', highlight ? SHADES[suitIndex * 2] : 5)
sprite(
tileX + 19,
tileY + 1,
SPRITES[suitIndex * 2 + 1],
highlight ? -SHADES[suitIndex * 2] : -5
)
})
range(0, VALUES.length / 2).forEach(valueIndex => {
const tileX = x + 12
const tileY = 32 + y + valueIndex * GRID_H * 2
const highlight = currentBet - 2 === valueIndex
rectFill(tileX, tileY, 11, GRID_H * 2 - 1, highlight ? 0 : 4)
!highlight && rectStroke(tileX, tileY, 11, GRID_H * 2 - 1, 5)
print(tileX + 4, tileY + 6, 1 + valueIndex * 2, highlight ? 6 : 5)
print(tileX + 2, tileY + 13, 'or', highlight ? 6 : 5)
print(tileX + 4, tileY + 20, 1 + valueIndex * 2 + 1, highlight ? 6 : 5)
})
VALUES.forEach((value, valueIndex) => {
const tileX = x + 20
const tileY = 32 + valueIndex * GRID_H
const highlight = currentBet - 9 == valueIndex
rectFill(tileX + 4, tileY, 7, GRID_H - 1, highlight ? 0 : 4)
!highlight && rectStroke(tileX + 4, tileY, 7, GRID_H - 1, 5)
print(tileX + 6, tileY + 5, value, 5)
})
SUITS.forEach((suit, suitIndex) => {
const tileX = 32 + x + suitIndex * GRID_W
const tileY = y + 20
const highlight = currentBet - 5 === suitIndex
rectFill(tileX, tileY, GRID_W - 1, 11, highlight ? 0 : 4)
!highlight && rectStroke(tileX, tileY, GRID_W - 1, 11, 5)
sprite(tileX + 3, tileY + 1, SPRITES[suit], highlight ? -SHADES[suit] : -5)
VALUES.forEach((value, valueIndex) => {
const tileY = 32 + y + valueIndex * GRID_H
const active = betMatches(bet, suit, value)
const highlight = active && currentBet >= GRID_BETS_START
rectFill(tileX, tileY, GRID_W - 1, GRID_H - 1, highlight ? 0 : 2)
rectStroke(
tileX,
tileY,
GRID_W - 1,
GRID_H - 1,
active ? (highlight ? 0 : 1) : 3
)
sprite(
tileX + 5,
tileY + 5,
SPRITES[suit],
highlight && currentBet >= GRID_BETS_START ? -SHADES[suit] : -5
)
print(tileX + 2, tileY + 2, value, highlight ? SHADES[suit] : 5)
})
})
}
const betMatches = (bet, suit, value) => {
let match = true
if (Array.isArray(bet.suits)) {
match = bet.suits.includes(suit)
}
if (match && Array.isArray(bet.values)) {
match = bet.values.includes(value)
}
return match
}
const drawBetAmount = ([x, y], state) => {
print(x + 10, y, 'Wager:')
BET_AMOUNTS.forEach((amount, amountIndex) => {
const current = amountIndex === state.currentBetAmount
const yOffset = 10 + y + amountIndex * 8
print(
x + 10,
yOffset,
amount + ' credit' + (amount !== 1 ? 's' : ''),
current ? 0 : 5
)
current && drawCursor(x - 2, yOffset - 2)
})
}
const drawCredits = ([x, y], credits) => {
print(x, y, `Credits:${credits}`)
}
const drawDealt = ([x, y], state) => {
print(x, y, 'Pick a card')
state.dealt.forEach(([suit, value], cardIndex) => {
const current = state.currentDealt === cardIndex
const offsetY = 8 + y + cardIndex * 26
drawCard(
[x + 6, offsetY],
!(state.gameState >= STATE_RESULT && current),
value,
suit
)
current && drawCursor(x - 2, offsetY + 8)
})
}
const drawCursor = (x, y) => {
sprite(x, y, SPRITE_CURSOR)
}
const drawResult = ([x, y], state) => {
if (state.win) {
print(x, y, 'You won')
print(x, y + 6, `+${state.win} money!`)
} else {
print(x, y, 'Better luck')
print(x, y + 6, 'Next time...')
}
}
const SPRITES_LOGO = [
[16, 17, 18, 19, 20, 21, 21, 20],
[32, 33, 34, 35, 36, 37, 37, 36]
]
const drawLogo = ([x, y]) => {
SPRITES_LOGO.forEach((sprites, row) =>
sprites.forEach((s, letter) => sprite(x + letter * 8, y + row * 8, s))
)
}
const drawInfo = ([x, y]) => {
;[
'Win credits by',
'guessing the card.',
'Better guesses give',
'better payouts. ',
'The deck is shuffled ',
'once it runs out.'
].forEach((row, rowIndex) => {
print(x, y + rowIndex * 7, row, 2)
})
}
draw = state => {
clear(7)
log(GRID_BETS_START)
drawDeck([0, 97], state.deck)
if (state.gameState === STATE_BET_AMOUNT) {
drawBetAmount([55, 32], state)
drawLogo([64, 0])
drawInfo([50, 73])
}
if (state.gameState >= STATE_BET_TYPE) {
drawBetGrid([32, 0], state)
}
if (state.gameState >= STATE_DEAL) {
drawDealt([0, 24], state)
}
if (state.gameState >= STATE_CONTINUE) {
drawResult([0, 84], state)
}
drawCredits([0, 1], state.credits)
}
{
"iframeVersion": "0.1.280",
"lines": [
434,
0,
0,
0,
0,
0,
0,
0
]
}
{
"0": [
" 0000000",
"00111111",
"01000000",
"01000000",
"01000000",
"01000000",
"01000000",
"01000000"
],
"1": [
"01000000",
"01000000",
"01000000",
"01000000",
"01000000",
"01000000",
"01000000",
"01000000"
],
"2": [
" 1111111",
"11444444",
"14222222",
"14223232",
"14232323",
"14223232",
"14232323",
"14223232"
],
"3": [
"14232323",
"14223232",
"14232323",
"14223232",
"14223232",
"14232323",
"14223232",
"14232323"
],
"4": [
" ",
" 00 00 ",
" 0000000",
" 0000000",
" 0000000",
" 00000 ",
" 000 ",
" 0 "
],
"5": [
" ",
" 0 ",
" 000 ",
" 00000 ",
" 0000000",
" 00000 ",
" 000 ",
" 0 "
],
"6": [
" 0 ",
" 000 ",
" 00000 ",
" 0000000",
" 0000000",
" 00000 ",
" 0 ",
" 000 "
],
"7": [
" ",
" 000 ",
" 000 ",
" 00 0 00",
" 0000000",
" 00 0 00",
" 0 ",
" 000 "
],
"8": [
" ",
" ",
" 1100 ",
" 14330 ",
" 143330",
" 14330 ",
" 1100 ",
" "
],
"16": [
" ",
" 000 ",
" 00 00 ",
" 00 00",
" 00 00",
" 00 ",
" 00 ",
" 00 "
],
"17": [
" ",
" 0 ",
" 0 ",
" 000 ",
" 0 0 ",
" 00 00 ",
" 0 0 ",
" 00 00"
],
"18": [
" ",
" 00000 ",
" 00 00 ",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00 "
],
"19": [
" ",
" 00000 ",
" 00 00 ",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00"
],
"20": [
" ",
" 0000000",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 "
],
"21": [
" ",
" 0000000",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 "
],
"32": [
" 00 ",
" 00 ",
" 00 ",
" 00 00",
" 00 00",
" 00 00 ",
" 000 ",
" "
],
"33": [
" 0000000",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" "
],
"34": [
" 00000 ",
" 00 00 ",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" "
],
"35": [
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00",
" 00 00 ",
" 00000 ",
" "
],
"36": [
" 00000 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 0000000",
" "
],
"37": [
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" 00 ",
" "
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment