diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ff9b29 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Klondike + +A game of patience from Klondike. + diff --git a/deck52.lua b/deck52.lua new file mode 100644 index 0000000..57d64ab --- /dev/null +++ b/deck52.lua @@ -0,0 +1,243 @@ +--************************************************************************************************* + +CARDW = 200 +CARDH = 300 + +Heart = 1 +Spade = 2 +Diamond = 3 +Club = 4 + +FaceUp = true +FaceDown = false + +backId = 54 + +TABLEAU_MARGIN_FACEUP = 80 +TABLEAU_MARGIN_FACEDOWN = 10 + +--************************************************************************************************* + +local lgni = love.graphics.newImage +local lgnq = love.graphics.newQuad +local lsgsc = love.graphics.setColor +local lgd = love.graphics.draw +local lgsc = love.graphics.setColor +local lgr = love.graphics.rectangle + +--************************************************************************************************* + +local cardAtlas = lgni("deck52english.png") +local cardAtlasW = cardAtlas:getWidth() +local cardAtlasH = cardAtlas:getHeight() + +local cardQuads = {} + +for i = 1, 57 do + cardQuads[i] = lgnq(((i-1) % 13) * CARDW, math.floor((i-1) / 13) * CARDH, CARDW, CARDH, cardAtlasW, cardAtlasH) +end + +--************************************************************************************************* + +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:draw() + lgsc(1, 1, 1, 1) + if (self.showing == FaceUp) then + lgd(cardAtlas, cardQuads[self.id], self.x, self.y) + else + lgd(cardAtlas, cardQuads[backId], self.x, self.y) + end + + lgsc(0.25, 0.25, 0.25, 1) + lgr("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 TABLEAU_MARGIN_FACEUP or TABLEAU_MARGIN_FACEDOWN) + end + else + for _, card in ipairs(self) do + card.x = self.x + card.y = self.y + end + + end +end + +--Fisher–Yates 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:indexOf(card) + if (#self == 0) then + return + end + + for i = #self, 1, -1 do + if (card == self[i]) then + return #self - i + end + end + + return math.huge +end + +function Deck:draw() + if (#self == 0) then + lgsc(1, 1, 0, 1) + lgr("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 or 0 + o.y = y or 0 + 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 + +--************************************************************************************************* + +return Deck, Card + diff --git a/mysa.png b/deck52english.png similarity index 100% rename from mysa.png rename to deck52english.png diff --git a/main.lua b/main.lua index c253c29..15b2ba8 100644 --- a/main.lua +++ b/main.lua @@ -1,233 +1,20 @@ -local min = math.min -local max = math.max +local lgsc = love.graphics.setColor +local lgsbgc = love.graphics.setBackgroundColor -local lg = love.graphics -lg.setDefaultFilter("nearest","nearest") - -local mx, my = 0, 0 -local mb = false - -local cardOffX, cardOffY = 0, 0 +local Deck, Card = dofile("deck52.lua") local marginX = 20 local marginY = 20 local padding = 10 -local cardAtlas = lg.newImage("mysa.png") -local cardAtlasW = cardAtlas:getWidth() -local cardAtlasH = cardAtlas:getHeight() - -local CARDW = 200 -local CARDH = 300 +local mx, my = 0, 0 +local mb = false +local cardOffX, cardOffY = 0, 0 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, cardAtlasW, cardAtlasH) -end - -local backId = 54 - -local Heart = 1 -local Spade = 2 -local Diamond = 3 -local Club = 4 - -local suitNames = { "Hearts", "Spade", "Diamonds", "Clubs" } - ---************************************************************************************************* - -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:draw() - lg.setColor(1, 1, 1, 1) - if (self.showing == FaceUp) then - lg.draw(cardAtlas, cardQuads[self.id], self.x, self.y) - else - lg.draw(cardAtlas, cardQuads[backId], self.x, self.y) - end - - 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 - ---Fisher–Yates 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 +love.window.setMode(slotX(9), slotY(5)) --************************************************************************************************* @@ -256,6 +43,11 @@ local decks = { tableau1, tableau2, tableau3, tableau4, tableau5, tableau6, tableau7, } +local tableaus = { tableau1, tableau2, tableau3, tableau4, tableau5, tableau6, tableau7 } +local foundations = { foundation1, foundation2, foundation3, foundation4 } + +--************************************************************************************************* + local findDeckUnderMouse = function(x, y) for _, deck in ipairs(decks) do if (#deck > 0) then @@ -269,31 +61,7 @@ local findDeckUnderMouse = function(x, y) end end ---[[ -local intersectionArea = function(left1, top1, right1, bottom1, left2, top2, right2, bottom2) - if (left2 < right1 and right2 > left1 and top2 < bottom1 and bottom2 > top1) then - return - min( r1->right, r2->right) - max(r1->x, r2->x) --Width of intersecting rect. - * - min(r1->bottom, r2->bottom) - max(r1->y, r2->y) --Height ... - end - - return 0 -end - -local findDeckUnderCard = function(card) - 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 @@ -309,6 +77,30 @@ local undo = function() undoDeck = nil 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 + +--************************************************************************************************* + +local isAnyOf = function(t, o) + for _, v in ipairs(t) do + if (v == o) then + return true + end + end +end + +--************************************************************************************************* + local newGame = function() for _, deck in ipairs(decks) do deck:empty() @@ -333,31 +125,26 @@ local newGame = function() end end +--************************************************************************************************* + love.load = function() math.randomseed(os.time()) - lg.setLineStyle("rough") - lg.setLineWidth(1) - lg.setPointSize(1) + 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 @@ -422,15 +209,7 @@ love.mousepressed = function(x, y, b) 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 @@ -496,6 +275,8 @@ love.mousereleased = function(x, y, b) hooverCard = nil end +--************************************************************************************************* + love.keypressed = function(k) if (k == "n") then newGame() @@ -506,6 +287,8 @@ love.keypressed = function(k) end end +--************************************************************************************************* + love.update = function(dt) hooverDeck, hooverCard = findDeckUnderMouse(mx, my) @@ -514,9 +297,11 @@ love.update = function(dt) end end +--************************************************************************************************* + love.draw = function() - lg.setColor(1, 1, 1, 1) - lg.setBackgroundColor(0.20, 0.40, 0.20, 1) + lgsc(1, 1, 1, 1) + lgsbgc(0.20, 0.40, 0.20, 1) for _, deck in ipairs(decks) do deck:draw()