Klondike/main.lua
Kari Sigurjonsson 1aa46678da Great Job.
2021-08-16 21:22:07 +00:00

542 lines
11 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local lg = love.graphics
lg.setDefaultFilter("nearest","nearest")
local mx, my = 0, 0
local mb = false
local cardOffX, cardOffY = 0, 0
local marginX = 20
local marginY = 20
local padding = 10
local cardImage = lg.newImage("mysa.png")
local cardImageW = cardImage:getWidth()
local cardImageH = cardImage:getHeight()
local CARDW = 200
local CARDH = 300
local slotX = function(v) return (v * CARDW) + ((v + 1) * padding) + marginX end
local slotY = function(v) return (v * CARDH) + ((v + 1) * padding) + marginY end
love.window.setMode(slotX(8), slotY(5))
local cardQuads = {}
for i = 1, 57 do
cardQuads[i] = lg.newQuad(((i-1) % 13) * CARDW, math.floor((i-1) / 13) * CARDH, CARDW, CARDH, cardImageW, cardImageH)
end
local backId = 54
local Heart = 1
local Spade = 2
local Diamond = 3
local Club = 4
local suitNames = {
"Hearts", "Spade", "Diamonds", "Clubs"
}
local writeln = function(...)
local t = {...}
for k, v in ipairs(t) do
t[k] = tostring(v)
end
io.stderr:write(table.concat(t, "\t"), "\n")
end
--*************************************************************************************************
local FaceUp = true
local FaceDown = false
local Card = {
id = 1,
x = 0,
y = 0,
w = CARDW,
h = CARDH
}
function Card:show()
self.showing = FaceUp
end
function Card:move(x, y)
self.x = x
self.y = y
end
function Card:hitTest(x, y)
return not (
(x < self.x) or (x > (self.x + self.w))
or (y < self.y) or (y > (self.y + self.h))
)
end
function Card:intersection(other)
--[[
left = max(r1.left, r2.left)
right = min(r1.right, r2.right)
bottom = max(r1.bottom, r2.bottom)
top = min(r1.top, r2.top)
Then, if intersection is not empty (left < right && bottom < top),
subtract it from the common area of two rectangles: r1.area + r2.area - intersection.area.
PS:
Assumption 1: rectangles are aligned by the coordinate axes, that's usually the case.
Assumption 2: y axis here increases upwards, for example, in a graphics application, the y axis increases downwards, you may need to use:
bottom = min(r1.bottom, r2.bottom)
top = max(r1.top, r2.top)
--]]
end
function Card:draw()
lg.setColor(1, 1, 1, 1)
if (self.showing == FaceUp) then
lg.draw(cardImage, cardQuads[self.id], self.x, self.y)
else
lg.draw(cardImage, cardQuads[backId], self.x, self.y)
end
--lg.setColor(0.05, 0.05, 0.05, 1)
--lg.setColor(0.15, 0.15, 0.15, 1)
lg.setColor(0.25, 0.25, 0.25, 1)
lg.rectangle("line", self.x, self.y, CARDW, CARDH, 4)
end
function Card:new(id, x, y, showing)
local o = {}
for k, v in pairs(self) do
o[k] = v
end
o.id = id
o.rank = math.floor(((id - 1) % 13) + 1);
o.suit = math.floor(((id - 1) / 13) + 1);
o.showing = showing
return o
end
--*************************************************************************************************
local Deck = {
x = 0,
y = 0
}
function Deck:empty()
local j = #self
for i = 1, j do
self[i] = nil
end
end
function Deck:push(card)
card.owner = self
card.x = self.x
card.y = self.y
self[#self + 1] = card
end
function Deck:layout()
if (self.tableau) then
local yoff = self.y
for _, card in ipairs(self) do
card.x = self.x
card.y = yoff
yoff = yoff + ((card.showing == FaceUp) and 38 or 10)
end
else
for _, card in ipairs(self) do
card.x = self.x
card.y = self.y
end
end
end
--FisherYates shuffle.
function Deck:shuffle()
for i = #self-1, 1, -1 do
j = math.random(0, i)
local tc = self[i+1]
self[i+1] = self[j+1]
self[j+1] = tc
end
end
function Deck:move(x, y)
self.x = x
self.y = y
for _, card in ipairs(self) do
card.x = x
card.y = y
end
self:layout()
end
function Deck:findCardUnderMouse(x, y)
if (#self == 0) then
return
end
for i = #self, 1, -1 do
local card = self[i]
if (card:hitTest(x, y)) then
return card
end
end
end
function Deck:hitTest(x, y)
return not (
(x < self.x) or (x > (self.x + self.w))
or (y < self.y) or (y > (self.y + self.h))
)
end
function Deck:draw()
if (#self == 0) then
lg.setColor(1, 1, 0, 1)
lg.rectangle("line", self.x, self.y, self.w, self.h, 15)
else
for _, card in ipairs(self) do
card:draw()
end
end
end
function Deck:topCard()
return self[#self]
end
function Deck:pop()
local o = self[#self]
self[#self] = nil
return o
end
function Deck:popAt(atCard)
if (#self == 0) then return end
local deck = Deck:new(mx, my, true);
local count = #self
for i = 1, count do
if (self[i] == atCard) then
for j = i, count do
deck:push(self[j])
self[j] = nil
end
break
end
end
return deck
end
function Deck:new(x, y, tableau, cards)
local o = {}
for k, v in pairs(self) do
o[k] = v
end
o.x = x
o.y = y
o.w = CARDW
o.h = CARDH
o.tableau = tableau
if (cards) then
for _, card in ipairs(cards) do
o:push(card)
end
end
return o
end
--*************************************************************************************************
local stock = Deck:new(slotX(0), slotY(0), false)
local waste = Deck:new(slotX(1), slotY(0), false)
local foundation1 = Deck:new(slotX(3), slotY(0), false)
local foundation2 = Deck:new(slotX(4), slotY(0), false)
local foundation3 = Deck:new(slotX(5), slotY(0), false)
local foundation4 = Deck:new(slotX(6), slotY(0), false)
local tableau1 = Deck:new(slotX(0), slotY(1), true)
local tableau2 = Deck:new(slotX(1), slotY(1), true)
local tableau3 = Deck:new(slotX(2), slotY(1), true)
local tableau4 = Deck:new(slotX(3), slotY(1), true)
local tableau5 = Deck:new(slotX(4), slotY(1), true)
local tableau6 = Deck:new(slotX(5), slotY(1), true)
local tableau7 = Deck:new(slotX(6), slotY(1), true)
local handDeck = nil
local undoDeck = nil
local decks = {
stock, waste,
foundation1, foundation2, foundation3, foundation4,
tableau1, tableau2, tableau3, tableau4, tableau5, tableau6, tableau7,
}
local findDeckUnderMouse = function(x, y)
for _, deck in ipairs(decks) do
if (#deck > 0) then
local card = deck:findCardUnderMouse(x, y)
if (card) then
return deck, card
end
elseif (deck:hitTest(x, y)) then
return deck
end
end
end
local undo = function()
if (not handDeck or not undoDeck) then return end
local count = #handDeck
for _, card in ipairs(handDeck) do
undoDeck:push(card)
end
undoDeck:layout()
handDeck = nil
undoDeck = nil
end
local newGame = function()
for _, deck in ipairs(decks) do
deck:empty()
end
for i = 1, 52 do
stock:push(Card:new(i, 0, 0, FaceDown))
end
stock:shuffle()
for i = 1, 7 do
for j = 1, i do
local card = stock:pop()
card.showing = (i == j)
decks[6 + i]:push(card)
end
end
for _, deck in ipairs(decks) do
deck:layout()
end
end
love.load = function()
math.randomseed(os.time())
love.graphics.setLineStyle("rough")
love.graphics.setLineWidth(1)
love.graphics.setPointSize(1)
newGame()
end
love.mousemoved = function(x, y)
mx = x
my = y
end
local tableaus = { tableau1, tableau2, tableau3, tableau4, tableau5, tableau6, tableau7 }
local foundations = { foundation1, foundation2, foundation3, foundation4 }
local isAnyOf = function(t, o)
for _, v in ipairs(t) do
if (v == o) then
return true
end
end
end
love.mousepressed = function(x, y, b)
if (b == 1) then
if (hooverDeck == waste) then
if (hooverCard) then
undoDeck = waste
cardOffX = x - hooverCard.x
cardOffY = y - hooverCard.y
handDeck = Deck:new(x - cardOffX, y - cardOffY, true)
local card = hooverDeck:pop()
card.showing = FaceUp
handDeck:push(card)
handDeck:layout()
end
elseif (hooverDeck == stock) then
if (#stock == 0) then
--Click on empty stock restores it from waste.
local card = waste:pop()
while card do
card.showing = FaceDown
stock:push(card)
card = waste:pop()
end
stock:shuffle()
stock:layout()
waste:layout()
else
--Click on face down card moves it face-up to waste.
local card = stock:pop()
card.showing = FaceUp
waste:push(card)
waste:layout()
stock:layout()
end
-- handDeck = nil
elseif (hooverCard and (hooverCard == hooverDeck:topCard()) and (hooverCard.showing == FaceDown)) then
--Click on face down card flips it.
hooverCard.showing = FaceUp
hooverDeck:layout()
elseif (hooverCard and hooverCard.showing == FaceUp) then
undoDeck = hooverDeck
cardOffX = x - hooverCard.x
cardOffY = y - hooverCard.y
handDeck = Deck:new(x - cardOffX, y - cardOffY, true, hooverDeck:popAt(hooverCard))
hooverDeck:layout()
end
elseif (b == 2) then
if (hooverCard and (hooverCard == hooverDeck:topCard()) and (hooverCard.showing == FaceUp)) then
for k, deck in ipairs(foundations) do
local tc = deck:topCard()
if ((#deck == 0 and hooverCard.rank == 1) or (tc and hooverCard.rank == tc.rank + 1 and hooverCard.suit == tc.suit)) then
local card = hooverDeck:pop()
card.showing = FaceUp
deck:push(card)
return --RETURN
end
end
end
end
end
local isOddCard = function(card1, card2)
local suit1 = card1.suit
local suit2 = card2.suit
return (
(((suit1 == Spade) or (suit1 == Club)) and ((suit2 == Heart) or (suit2 == Diamond)))
or (((suit1 == Heart) or (suit1 == Diamond)) and ((suit2 == Spade) or (suit2 == Club)))
)
end
love.mousereleased = function(x, y, b)
if (b ~= 1) then return end
if (handDeck and hooverDeck) then
if (isAnyOf(tableaus, hooverDeck)) then
if (#hooverDeck == 0) then
--Drop on empty tableau, only king allowed.
if (handDeck[1].rank == 13) then
local count = #handDeck
for _, card in ipairs(handDeck) do
hooverDeck:push(card)
end
else
undo()
end
elseif (hooverDeck:topCard() == hooverCard) then
--Must drop on the top card and only odd-colored down-going cards allowed.
if (
isOddCard(hooverDeck[#hooverDeck], handDeck[1])
and (handDeck[1].rank == hooverCard.rank - 1)
) then
for _, card in ipairs(handDeck) do
hooverDeck:push(card)
end
else
undo()
end
else
undo()
end
hooverDeck:layout()
elseif (#handDeck == 1 and isAnyOf(foundations, hooverDeck)) then
--Drop on foundation, only one card at a time. Only up-going same sort allowed.
if (#hooverDeck == 0) then
--Drop on empty foundation. Only aces allowed.
if (handDeck[1].rank == 1) then
hooverDeck:push(handDeck[1])
else
undo()
end
elseif (
hooverDeck[#hooverDeck].rank == handDeck[1].rank - 1
and hooverDeck[#hooverDeck].suit == handDeck[1].suit
) then
hooverDeck:push(handDeck[1])
else
undo()
end
else
undo()
end
hooverDeck:layout()
elseif (handDeck) then
undo()
end
handDeck = nil
undoDeck = nil
hooverDeck = nil
hooverCard = nil
end
love.keypressed = function(k)
if (k == "f1") then
newGame()
elseif (k == "f3") then
backId = 53
elseif (k == "f4") then
backId = 54
end
end
love.update = function(dt)
hooverDeck, hooverCard = findDeckUnderMouse(mx, my)
if (handDeck) then
handDeck:move(mx - cardOffX, my - cardOffY)
end
end
love.draw = function()
lg.setColor(1, 1, 1, 1)
lg.setBackgroundColor(0.20, 0.40, 0.20, 1)
for _, deck in ipairs(decks) do
deck:draw()
end
if (handDeck) then
handDeck:draw()
end
end