intitial commit
This commit is contained in:
3
backend.js
Normal file
3
backend.js
Normal file
@@ -0,0 +1,3 @@
|
||||
class BackendConnection {
|
||||
|
||||
}
|
||||
75
card-v2.js
Normal file
75
card-v2.js
Normal file
@@ -0,0 +1,75 @@
|
||||
class Card {
|
||||
#suit
|
||||
#rank
|
||||
#value
|
||||
#faceUp
|
||||
#color
|
||||
|
||||
constructor(suit, rank, vis=false) {
|
||||
/*Create an object of class Card; provide suit, rank, [visible]*/
|
||||
this.#suit = suit;
|
||||
this.#rank = rank;
|
||||
this.#faceUp = vis;
|
||||
this.#setColor();
|
||||
|
||||
switch(this.#rank) {
|
||||
case 'J':
|
||||
this.#value = 11;
|
||||
break;
|
||||
case 'Q':
|
||||
this.#value = 12;
|
||||
break;
|
||||
case 'K':
|
||||
this.#value = 13;
|
||||
break;
|
||||
case 'A':
|
||||
this.#value = 1;
|
||||
break;
|
||||
default:
|
||||
this.#value = parseInt(this.#rank);
|
||||
}
|
||||
}
|
||||
|
||||
#setColor() {
|
||||
if (this.#suit[0] == "H" || this.#suit[0] == "D") {
|
||||
this.#color = "red";
|
||||
}
|
||||
else {
|
||||
this.#color = "black";
|
||||
}
|
||||
}
|
||||
|
||||
flip() {
|
||||
/*Inverts the card's visibility*/
|
||||
this.#faceUp = !this.#faceUp;
|
||||
}
|
||||
|
||||
getVisible() {
|
||||
/*Returns a Boolean; True if the card face is visible*/
|
||||
return this.#faceUp;
|
||||
}
|
||||
getValue() {
|
||||
/*Returns the card's equivalent integer value; Ace is 14*/
|
||||
return this.#value;
|
||||
}
|
||||
|
||||
getRank() {
|
||||
/*Returns the card's rank*/
|
||||
return this.#rank;
|
||||
}
|
||||
|
||||
getSuit() {
|
||||
/*Returns the card's suit*/
|
||||
return this.#suit;
|
||||
}
|
||||
|
||||
getColor() {
|
||||
/*Returns the card's color*/
|
||||
return this.#color;
|
||||
}
|
||||
|
||||
getImageRef() {
|
||||
/*Returns a string containing a local filename for the card*/
|
||||
return `${this.#rank + this.#suit}.png`;
|
||||
}
|
||||
}
|
||||
33
cardgame.html
Normal file
33
cardgame.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html {
|
||||
background-color: #808080;
|
||||
}
|
||||
/*canvas styling*/
|
||||
#canvas {
|
||||
position: absolute;
|
||||
top:0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
aspect-ratio: 16/9;
|
||||
margin:auto;
|
||||
border: solid rgb(400,400,300) 4px;
|
||||
}
|
||||
</style>
|
||||
<title>Solitaire - Klondike - Yurun Chen</title>
|
||||
<!-- <link rel="stylesheet" href="style.css"> -->
|
||||
<script type="text/javascript" defer src="card-v2.js"></script>
|
||||
<script type="text/javascript" defer src="deck-v2.js"></script>
|
||||
<!-- main game javascript file-->
|
||||
<script type="text/javascript" defer src="game.js"></script>
|
||||
<!-- not currently in use-->
|
||||
<script type="text/javascript" defer src="backend.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas" width="400" height="300"> </canvas>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
62
deck-v2.js
Normal file
62
deck-v2.js
Normal file
@@ -0,0 +1,62 @@
|
||||
class Deck {
|
||||
#deck
|
||||
|
||||
constructor() {
|
||||
/*Initialises a new shuffled deck of 52 cards*/
|
||||
this.#deck = [];
|
||||
this.createDeck();
|
||||
this.shuffle();
|
||||
}
|
||||
|
||||
createDeck() {
|
||||
/*Creates an array of 52 Card objects*/
|
||||
const suits = ['H', 'S', 'C', 'D'];
|
||||
const faces = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 13; j++) {
|
||||
this.#deck.push(new Card(suits[i], faces[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shuffle() {
|
||||
/*Randomly shuffles the deck*/
|
||||
for (let i = this.getSize() - 1; i > 0; i--) {
|
||||
let j = Math.floor(Math.random() * (i + 1));
|
||||
var temp = this.#deck[i];
|
||||
this.#deck[i] = this.#deck[j];
|
||||
this.#deck[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
getSize() {
|
||||
/*Returns the number of elements in the deck*/
|
||||
return this.#deck.length;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
/*Returns a Boolean; True if the deck is currently empty*/
|
||||
return this.getSize() < 1;
|
||||
}
|
||||
|
||||
viewTop() {
|
||||
/*Returns the topmost card without removing it from the deck, or null if the deck is empty*/
|
||||
if (!this.isEmpty()) {
|
||||
return this.#deck[this.getSize() - 1]
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
drawCard() {
|
||||
/*Returns the topmost card in the deck, or null if the deck is empty*/
|
||||
if (!this.isEmpty()) {
|
||||
return this.#deck.pop();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
387
game.js
Normal file
387
game.js
Normal file
@@ -0,0 +1,387 @@
|
||||
|
||||
//column class
|
||||
class Column extends Stack{
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
//stock stack
|
||||
class Stock extends Stack {
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
|
||||
//append card back to stock after wastepile exceeds threshold CRUDE
|
||||
append(card){
|
||||
var items = super.get_items();
|
||||
items.unshift(card);
|
||||
super.set_items(items);
|
||||
}
|
||||
}
|
||||
|
||||
class WastePile extends Stack {
|
||||
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
//clearing waste pile by popping the cards one by one
|
||||
clear () {
|
||||
for (i=super.size();i--;i>-1){
|
||||
super.pop();
|
||||
}
|
||||
}
|
||||
|
||||
//determines if the waste pile has met the threshold of 3
|
||||
threshold (){
|
||||
if (super.size() == 3){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Object for board layout
|
||||
class Board {
|
||||
#waste_pile
|
||||
#stock_pile
|
||||
#foundations
|
||||
#tableau
|
||||
#deck
|
||||
|
||||
constructor (){
|
||||
//init a deck
|
||||
this.#deck = new Deck();
|
||||
this.#tableau = new Array();
|
||||
this.#stock_pile = new Stock();
|
||||
this.#waste_pile = new WastePile();
|
||||
//initialise the 7 columns
|
||||
for (var i=1; i<8; i++){
|
||||
this.#tableau.push(new Column());
|
||||
}
|
||||
|
||||
//initialise the value in the 7 columns
|
||||
for (var j = 1; j<8; j++){
|
||||
for (var k = j; k>0; k--){
|
||||
this.#tableau[j-1].push(this.#deck.drawCard());
|
||||
}
|
||||
}
|
||||
|
||||
var length = this.#deck.getSize();
|
||||
//initialise the stock pile
|
||||
for (var k=0; k <= length; k++){
|
||||
this.#stock_pile.push(this.#deck.drawCard());
|
||||
}
|
||||
|
||||
//reveal top card except stock pile, otherwise it'd be cheating
|
||||
|
||||
}
|
||||
|
||||
get_stock(){
|
||||
return this.#stock_pile;
|
||||
}
|
||||
|
||||
get_tableau(){
|
||||
return this.#tableau;
|
||||
}
|
||||
}
|
||||
|
||||
//base class for element
|
||||
//Elements are required to implement
|
||||
class Element{
|
||||
constructor(x,y,dx,dy) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.dx = dx
|
||||
this.dy = dy
|
||||
this.ox = x;
|
||||
this.oy = y;
|
||||
this.odx = dx;
|
||||
this.ody = dy;
|
||||
}
|
||||
}
|
||||
|
||||
//View of background, really just shoved into a view, very simple
|
||||
class Background extends Element {
|
||||
|
||||
constructor(){
|
||||
super(0,0,1920,1080);
|
||||
}
|
||||
|
||||
resize(scale){
|
||||
this.dx = this.odx * scale[0];
|
||||
this.dy = this.ody * scale[1];
|
||||
}
|
||||
|
||||
render(ctx){
|
||||
ctx.fillStyle = "#168a22";
|
||||
ctx.rect(this.x, this.y, this.dx, this.dy);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//view class for different views to extend on
|
||||
class View {
|
||||
#element_list
|
||||
|
||||
constructor(element_list){
|
||||
this.#element_list = element_list;
|
||||
}
|
||||
|
||||
resize(scale){
|
||||
for (var i=0; i< this.#element_list.length; i++) {
|
||||
this.#element_list[i].resize(scale);
|
||||
}
|
||||
}
|
||||
|
||||
render(ctx){
|
||||
for (var i=0; i< this.#element_list.length; i++) {
|
||||
this.#element_list[i].render(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
add_element(e){
|
||||
this.#element_list.append(e);
|
||||
}
|
||||
}
|
||||
|
||||
//view for single card, bad naming I suppose, as it is not a view (really) more of a element
|
||||
class CardView extends Element {
|
||||
#revealed
|
||||
#img
|
||||
#ox
|
||||
#oy
|
||||
#odx
|
||||
#ody
|
||||
constructor(revealed, x, y, card, dx, dy){
|
||||
super(x,y,dx,dy);
|
||||
this.#ox = x;
|
||||
this.#oy = y;
|
||||
this.#odx = dx;
|
||||
this.#ody = dy;
|
||||
this.#revealed = revealed;
|
||||
this.#img = new Image();
|
||||
this.card = card;
|
||||
if (revealed) {
|
||||
this.#img.src = "cards/"+this.card.getRank()+Array.from(this.card.getSuit())[0]+".png";
|
||||
} else {
|
||||
this.#img.src = "cards/cardBackRed.png";
|
||||
}
|
||||
}
|
||||
|
||||
render(ctx){
|
||||
ctx.drawImage(this.#img, this.x, this.y,this.dx , this.dy);
|
||||
}
|
||||
|
||||
resize(scale) {
|
||||
this.x = this.ox*scale[1];
|
||||
this.dx = this.odx*scale[1];
|
||||
this.dy = this.odx*scale[0];
|
||||
this.y = this.oy*scale[1];
|
||||
}
|
||||
}
|
||||
|
||||
//0 orientation for landscape 1 orientation for portrait
|
||||
class StackView extends View{
|
||||
#orientation
|
||||
constructor (orientation,element_list){
|
||||
super(element_list)
|
||||
this.#orientation = orientation;
|
||||
}
|
||||
|
||||
render(ctx){
|
||||
super.render(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//view for the stock stack
|
||||
class StockView extends StackView{
|
||||
#card_list
|
||||
#stack
|
||||
constructor (stack){
|
||||
super(0, [new CardView(false,50,20, stack[-1], 200, 300)]);
|
||||
this.#stack = stack;
|
||||
}
|
||||
|
||||
render (ctx) {
|
||||
super.render(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//view for the waste stack
|
||||
class WasteView extends StackView{
|
||||
constructor(){
|
||||
super(0);
|
||||
}
|
||||
}
|
||||
|
||||
//view for the vertical columns
|
||||
class ColumnView extends StackView {
|
||||
|
||||
#column_array
|
||||
|
||||
constructor(element_list, arrays){
|
||||
super(0,element_list);
|
||||
this.#column_array = arrays;
|
||||
}
|
||||
|
||||
render(ctx){
|
||||
super.render(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//view for the foundations
|
||||
class FoundationView extends StackView {
|
||||
constructor(){
|
||||
super(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TableauView{
|
||||
//passed board object to gen view
|
||||
constructor (board){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//canvas rendering manager
|
||||
class RenderMgr{
|
||||
|
||||
#ctx
|
||||
#views
|
||||
#cur_view
|
||||
|
||||
//valid canvas context must be passed
|
||||
constructor(ctx){
|
||||
this.#ctx = ctx;
|
||||
this.#cur_view;
|
||||
}
|
||||
|
||||
resize(scale){
|
||||
//resize every single element present on view
|
||||
for (var i=0; i<this.#cur_view.length;i++){
|
||||
this.#cur_view[i].resize(scale);
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
//LOOP THROUGH RENDER IN VIEWS WHICH IN TURN LOOPS THROUGH TO RENDER ELEMENTS
|
||||
for (var i=0;i<this.#cur_view.length;i++){
|
||||
this.#cur_view[i].render(this.#ctx);
|
||||
}
|
||||
|
||||
requestAnimationFrame(this.render.bind(this))
|
||||
}
|
||||
|
||||
//switching cur_view lists, not really used extensively
|
||||
switch_view(view) {
|
||||
this.#cur_view = view;
|
||||
}
|
||||
|
||||
generate_column_view(tableau){
|
||||
var column_view = new Array();
|
||||
var elements = new Array();
|
||||
for (var i=0; i<tableau.length;i++){
|
||||
var element_list = new Array();
|
||||
var cx = 50;
|
||||
var cy = 240;
|
||||
var cdx = 180
|
||||
var cdy = 270;
|
||||
for (var j=0; j < tableau[i].size()+1; j++){
|
||||
var card = tableau[i].pop()
|
||||
//determine if the current card the is last card
|
||||
if (j==tableau[i].size()){
|
||||
element_list.unshift(new CardView(true, cx, cy,card, cdx, cdy));
|
||||
} else {
|
||||
element_list.unshift(new CardView(false, cx, cy, card,cdx,cdy));
|
||||
}
|
||||
//stack em on top
|
||||
cdy+=20;
|
||||
}
|
||||
column_view.push(new ColumnView(element_list, column_view));
|
||||
|
||||
//add offset for next column
|
||||
cx += 93.3;
|
||||
}
|
||||
|
||||
|
||||
//join the 2d aray into single array because element list, the arrays will be maintained separately
|
||||
|
||||
console.log(elements);
|
||||
return column_view
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class KlondikeGame {
|
||||
|
||||
#size_modifier
|
||||
#ctx
|
||||
#scale_modifier
|
||||
#mgr
|
||||
#stock_view
|
||||
#bg_view
|
||||
#column_view
|
||||
#deck
|
||||
|
||||
constructor (){
|
||||
//intialise a new deck
|
||||
this.#deck = new Deck();
|
||||
console.log("initialised deck");
|
||||
//grab context for render manager
|
||||
this.#ctx = document.getElementById("canvas").getContext("2d");
|
||||
console.log(this.#board);
|
||||
//initialise render manager
|
||||
this.#mgr = new RenderMgr(this.#ctx);
|
||||
//bind render function of rendermanager
|
||||
//initialise stock view
|
||||
this.#stock_view = new StockView(this.#board.get_stock());
|
||||
//initialise background
|
||||
this.#bg_view = new View([new Background()]);
|
||||
//initialise the 7 columns
|
||||
var tableau = this.#board.get_tableau();
|
||||
this.#column_view = this.#mgr.generate_column_view(tableau);
|
||||
console.log(this.#column_view);
|
||||
|
||||
|
||||
//switch view to deck TODO: ALL DECKS AND MENUBAR
|
||||
this.#mgr.switch_view([this.#bg_view,this.#stock_view, this.#column_view]);
|
||||
requestAnimationFrame(this.#mgr.render.bind(this.#mgr));
|
||||
|
||||
}
|
||||
|
||||
//update scale modifier, however only currently fixed to 16:9 aspect ratio.
|
||||
//all values calculated from modifier * 1920, 1080
|
||||
update_scale(resolution){
|
||||
this.#scale_modifier = [Number(resolution[0]/1920), Number(resolution[1]/1080)];
|
||||
this.#mgr.resize(this.#scale_modifier);
|
||||
}
|
||||
|
||||
switch_deck_view(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
var game_instance = new KlondikeGame();
|
||||
|
||||
window.onload = window.onresize = function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
canvas.width = window.innerWidth * 0.99;
|
||||
canvas.height = window.innerHeight * 0.9 ;
|
||||
game_instance.update_scale([canvas.width, canvas.height])
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user