Commit 973b2fb4 authored by kmohiuddin's avatar kmohiuddin

add to gitlab

parents
{
"directory": "app/bower_components"
}
\ No newline at end of file
{
"globals": {
"angular": false,
"_": false
},
"rules": {
"no-use-before-define": [
2,
"nofunc"
]
}
}
\ No newline at end of file
logs/*
!.gitkeep
node_modules/
bower_components/
tmp
.DS_Store
test-results/
## Directory-based project format:
.idea/
FROM nginx:1.10.1-alpine
ADD app/ /usr/share/nginx/html
\ No newline at end of file
stage 'CI'
node {
checkout scm
//git branch: 'jenkins2-course',
// url: 'https://github.com/g0t4/solitaire-systemjs-course'
// pull dependencies from npm
// on windows use: bat 'npm install'
sh 'npm install'
// stash code & dependencies to expedite subsequent testing
// and ensure same code & dependencies are used throughout the pipeline
// stash is a temporary archive
stash name: 'everything',
excludes: 'test-results/**',
includes: '**'
// test with PhantomJS for "fast" "generic" results
// on windows use: bat 'npm run test-single-run -- --browsers PhantomJS'
sh 'npm run test-single-run -- --browsers PhantomJS'
// archive karma test results (karma is configured to export junit xml files)
step([$class: 'JUnitResultArchiver',
testResults: 'test-results/**/test-results.xml'])
}
stage 'Browser Testing'
parallel chrome: {
runTests("Chrome")
}, firefox: {
runTests("Firefox")
}
def runTests(browser) {
node {
// on windows use: bat 'del /S /Q *'
sh 'rm -rf *'
unstash 'everything'
// on windows use: bat "npm run test-single-run -- --browsers ${browser}"
sh "npm run test-single-run -- --browsers ${browser}"
echo "Browser: $browser"
sh 'ls'
step([$class: 'JUnitResultArchiver',
testResults: 'test-results/**/test-results.xml'])
}
}
node('linux_agent1') {
sh 'ls'
sh 'rm -rf *'
unstash 'everything'
sh 'ls'
}
stage "Ready to Deploy"
input "Deploy to staging?"
stage name: "Deploy to staging", concurrency: 1
node{
// write build number to index page so we can see this update
// on windows use: bat "echo '<h1>${env.BUILD_DISPLAY_NAME}</h1>' >> app/index.html"
sh "echo '<h1>${env.BUILD_DISPLAY_NAME}</h1>' >> app/index.html"
// deploy to a docker container mapped to port 3000
// on windows use: bat 'docker-compose up -d --build'
sh 'docker-compose up -d --build'
notify 'Solitaire Deployed!'
}
def notify(status){
emailext (
to: "ibrahim@gmail.com",
subject: "${status}: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: """<p>${status}: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':</p>
<p>Check console output at <a href='${env.BUILD_URL}'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a></p>""",
)
}
Note: card images - see license in app/cards/images/0_LICENSE.txt or http://www.jfitz.com/cards/
The MIT License
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
{
"env": {
"browser": true,
"node": false
}
}
\ No newline at end of file
(function () {
"use strict";
angular.module("solitaire", ["klondike", "ngDraggable"]);
})();
<div ng-if="card">
<img ng-if="card.turnedUp"
ng-src="cards/images/{{card.image}}"
/>
<div ng-if="!card.turnedUp"
class="turned-down-card"
></div>
</div>
\ No newline at end of file
function Card(card) {
this.suit = card.suit;
this.rank = card.rank;
this.image = (Card.ranksInImagesOrder.indexOf(this.rank) * 4 + Card.suitsInImagesOrder.indexOf(this.suit) + 1) + ".png";
this.color = card.suit === "Spades" || card.suit === "Clubs" ? "black" : "red";
this.turnedUp = false;
}
Card.ranksInImagesOrder = ["Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2"];
Card.suitsInImagesOrder = ["Clubs", "Spades", "Hearts", "Diamonds"];
Card.prototype.turnUp = function () {
this.turnedUp = true;
};
Card.prototype.turnDown = function () {
this.turnedUp = false;
};
function Deck() {
this.unShuffled = function unShuffled() {
return _.chain(Card.ranksInImagesOrder)
.map(function (rank) {
return Card.suitsInImagesOrder.map(function (suit) {
return {
suit: suit,
rank: rank
};
});
})
.flatten()
.map(function (card) {
return new Card(card);
})
.value();
};
this.shuffled = function shuffled() {
return _.shuffle(this.unShuffled());
};
}
\ No newline at end of file
http://www.jfitz.com/cards/
These images were created using GIFCon, XnView and Paint Shop Pro.
Feel free to use for personal or professional purposes, subject to the following:
Additional Copyright information:
*Larry Ewing <lewing@isc.tamu.edu> created Tux using GIMP.
*Marshall Kirk McKusick <mckusick@mckusick.com> is the copyright holder and creator of the BSD Daemon image.
*The "Windows" cards were originally designed by Susan Kare for Microsoft.
To the best of my knowledge, the images used in any "standard" French/British 52 card deck are public domain.
*The top Jokers are derived from an image I found of a John Waddington design.
*The bottom Jokers were created by me as quick placeholders for a design I never got around to implementing.
*I guess it's possible that Microsoft could claim copyright on the Windows Ace of Spades.
*If they did, I'd suggest filling in or changing the white pattern on the Spade.
*The game of solitaire did not require the cards mentioned in these additional copyright notices
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" ng-app="solitaire" class="no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Solitaire</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="klondike/game.css">
</head>
<body>
<div ng-view></div>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="bower_components/underscore/underscore.js"></script>
<script src="bower_components/ngDraggable/ngDraggable.js"></script>
<script src="app.js"></script>
<script src="klondike/piles/pile.js"></script>
<script src="klondike/piles/remainderPile.js"></script>
<script src="klondike/piles/tableauPile.js"></script>
<script src="klondike/piles/foundationPile.js"></script>
<script src="klondike/scoring.js"></script>
<script src="klondike/klondike.js"></script>
<script src="klondike/board.js"></script>
<script src="klondike/game.js"></script>
<script src="cards/cards.js"></script>
</body>
</html>
<p>
<button ng-click="game.newGame()">New Game</button>
Score: {{ scoring.score }}
</p>
<div class="top-row">
<s-foundation ng-repeat="foundation in game.foundations"></s-foundation>
<s-no-pile></s-no-pile>
<s-no-pile></s-no-pile>
<s-remainder></s-remainder>
<s-waste></s-waste>
</div>
<div class="tableau-row">
<s-no-pile></s-no-pile>
<s-tableau ng-repeat="tableau in game.tableaus"></s-tableau>
</div>
\ No newline at end of file
(function () {
"use strict";
angular.module("klondike.board", ["ngRoute", "klondike.game"])
.config(["$routeProvider", function ($routeProvider) {
$routeProvider
.when("/board", {
templateUrl: "klondike/board.html",
controller: "KlondikeController"
})
.otherwise({redirectTo: "/board"});
}])
.controller("KlondikeController", ["$scope", "klondikeGame", "scoring", function KlondikeController($scope, klondikeGame, scoring) {
klondikeGame.newGame();
$scope.game = klondikeGame;
$scope.scoring = scoring;
}])
.directive("sNoPile", function () {
return {
restrict: "E",
template: "<div class=\"no-pile\"></div>"
};
})
.directive("sTableau", function () {
return {
restrict: "E",
templateUrl: "klondike/piles/tableau.html"
};
})
.directive("sFoundation", function () {
return {
restrict: "E",
templateUrl: "klondike/piles/foundation.html"
};
})
.directive("sCard", function () {
return {
restrict: "A",
templateUrl: "cards/card.html",
scope: {
card: "="
}
};
})
.directive("sRemainder", function () {
return {
restrict: "E",
templateUrl: "klondike/piles/remainder.html"
};
})
.directive("sWaste", function () {
return {
restrict: "E",
templateUrl: "klondike/piles/waste.html"
};
});
})();
body {
padding-left: 50px;
padding-right: 50px;
background: #B1CB9B;
}
div.pile,
div.no-pile {
width: 72px;
height: 96px;
margin-right: 10px;
float: left;
}
div.pile {
background-color: #CCE1A9;
}
.turned-down-card {
background: url('../cards/images/b1fv.png');
width: 72px;
height: 96px;
float: left;
}
.only-show-top-of-card {
width: 72px;
height: 16px;
float: left;
}
.tableau-row {
padding-top: 50px;
clear: both;
}
\ No newline at end of file
(function () {
"use strict";
angular.module("klondike.game", [])
.service("klondikeGame", ["scoring", KlondikeGame]);
function KlondikeGame(scoring) {
this.newGame = function newGame() {
var cards = new Deck().shuffled();
this.newGameFromDeck(cards);
};
this.newGameFromDeck = function (cards) {
scoring.newGame();
turnAllCardsDown(cards);
this.tableaus = dealTableaus(cards);
this.foundations = buildFoundations();
this.remainder = dealRemainder(cards);
};
function turnAllCardsDown(cards) {
cards.forEach(function (card) {
card.turnDown();
});
}
function dealTableaus(cards) {
var tableaus = [
new TableauPile(cards.slice(0, 1), scoring),
new TableauPile(cards.slice(1, 3), scoring),
new TableauPile(cards.slice(3, 6), scoring),
new TableauPile(cards.slice(6, 10), scoring),
new TableauPile(cards.slice(10, 15), scoring),
new TableauPile(cards.slice(15, 21), scoring),
new TableauPile(cards.slice(21, 28), scoring)
];
tableaus.forEach(function (tableau) {
tableau.turnTopCardUp();
});
return tableaus;
}
function buildFoundations() {
return _.range(1, 5)
.map(function () {
return new FoundationPile([], scoring);
});
}
function dealRemainder(cards) {
return new RemainderPile(cards.slice(28), scoring);
}
}
KlondikeGame.prototype.tryMoveTopCardToAnyFoundation = function (sourcePile) {
if (sourcePile.isEmpty()) {
return;
}
var foundationThatWillAccept = _.find(this.foundations, function (foundation) {
return foundation.willAcceptCard(sourcePile.topCard());
});
if (foundationThatWillAccept) {
foundationThatWillAccept.moveCardsFrom(sourcePile);
}
};
})();
angular.module("klondike", [
"klondike.game",
"klondike.board",
"klondike.scoring"
]);
<div class="pile"
ng-drop="true" ng-drop-success="foundation.moveCardsFrom($data)">
<div s-card card="foundation.topCard()"
ng-drag="true" ng-drag-data="foundation">
</div>
</div>
\ No newline at end of file
function FoundationPile(cards, scoring) {
Pile.call(this, cards, scoring);
}
FoundationPile.prototype = Object.create(Pile.prototype);
FoundationPile.prototype.constructor = FoundationPile;
FoundationPile.prototype.willAcceptCard = function (card) {
if (this.isEmpty()) {
return card.rank === "Ace";
}
var topCard = this.topCard();
var nextRank = Pile.increasingRanks[Pile.increasingRanks.indexOf(topCard.rank) + 1];
return topCard.suit === card.suit
&& card.rank === nextRank;
};
FoundationPile.prototype.drop = function (card) {
if (this.willAcceptCard(card)) {
this.addTopCard(card);
return true;
}
return false;
};
function Pile(cards, scoring) {
this.cards = cards || [];
this.scoring = scoring;
}
Pile.prototype.isEmpty = function () {
return this.cards.length === 0;
};
Pile.prototype.topCard = function () {
return this.cards[this.cards.length - 1];
};
Pile.prototype.removeCard = function (card) {
var index = this.cards.indexOf(card);
this.cards.splice(index, 1);
};
Pile.prototype.addTopCard = function (card) {
this.cards.push(card);
};
Pile.prototype.moveTopCardFrom = function (source) {
var topOfSource = source.topCard();
var accepted = this.drop(topOfSource);
if (accepted) {
this.scoring.dropped(source.constructor, this.constructor);
source.removeCard(topOfSource);
}
};
Pile.prototype.moveCardsFrom = Pile.prototype.moveTopCardFrom;
Pile.increasingRanks = ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"];
\ No newline at end of file
<div class="pile"
ng-click="game.remainder.flipTopCardToWaste()">
<div s-card card="game.remainder.topCard()">
</div>
</div>
\ No newline at end of file
function WastePile(cards, scoring) {
Pile.call(this, cards, scoring);
}
WastePile.prototype = Object.create(Pile.prototype);
WastePile.prototype.constructor = WastePile;
function RemainderPile(cards, scoring) {
Pile.call(this, cards, scoring);
this.waste = new WastePile([], scoring);
}
RemainderPile.prototype = Object.create(Pile.prototype);
RemainderPile.prototype.constructor = RemainderPile;
RemainderPile.prototype.flipTopCardToWaste = function () {
var card = this.topCard();
if (card === undefined) {
recycleWaste.call(this);
this.scoring.wasteRecycled();
return;
}
flipToWaste.call(this, card);
};
function flipToWaste(card) {
this.removeCard(card);
this.waste.addTopCard(card);
card.turnUp();
}
function recycleWaste() {
this.cards = this.waste.cards.reverse();
this.waste = new WastePile([], this.scoring);
this.cards.forEach(function (card) {
card.turnDown();
});
}
\ No newline at end of file
<div class="pile"
ng-dblclick="game.tryMoveTopCardToAnyFoundation(tableau)"
ng-drop="true" ng-drop-success="tableau.moveCardsFrom($data)"
ng-style="{height: tableau.heightForDrop()}">
<div ng-repeat="card in tableau.cards"
s-card card="card"
ng-class="{ 'only-show-top-of-card': card != tableau.topCard() }"
ng-drag="card == tableau.topCard()" ng-drag-data="tableau">
</div>
</div>
\ No newline at end of file
function TableauPile(cards, scoring) {
Pile.call(this, cards, scoring);
}
TableauPile.prototype = Object.create(Pile.prototype);
TableauPile.prototype.constructor = TableauPile;
TableauPile.prototype.turnTopCardUp = function () {
this.topCard().turnUp();
};
TableauPile.prototype.willAcceptCard = function (card) {
if (this.isEmpty()) {
return card.rank === "King";
}
var topCard = this.topCard();
var nextLowerRank = Pile.increasingRanks[Pile.increasingRanks.indexOf(topCard.rank) - 1];
return card.rank === nextLowerRank &&
card.color !== topCard.color;
};
TableauPile.prototype.drop = function (card) {
if (this.willAcceptCard(card)) {
this.addTopCard(card);
return true;
}
return false;
};
TableauPile.prototype.removeCard = function (card) {
Pile.prototype.removeCard.call(this, card);
var topCard = this.topCard();
if (!topCard) {
return;
}
if (!topCard.turnedUp) {
this.scoring.tableauCardTurnedUp();
}
topCard.turnUp();
};
TableauPile.prototype.moveFromTableau = function (source) {
var destination = this;
var acceptIndex = _.findIndex(source.cards, function (card) {
return card.turnedUp && destination.willAcceptCard(card);
});
if (acceptIndex < 0) {
return;
}
var rest = _.rest(source.cards, acceptIndex);
rest.forEach(function (card) {
source.removeCard(card);
destination.addTopCard(card);
});
};
TableauPile.prototype.moveCardsFrom = function (source) {
if(source == null) {
return;
}
if (source instanceof TableauPile) {
this.moveFromTableau(source);
return;
}
this.moveTopCardFrom(source);
};
TableauPile.prototype.heightForDrop = function () {
return (96 + Math.max(0, this.cards.length - 1) * 16) + 'px';
};
\ No newline at end of file
<div class="pile"
ng-dblclick="game.tryMoveTopCardToAnyFoundation(game.remainder.waste)">
<div s-card card="game.remainder.waste.topCard()"
ng-drag="true" ng-drag-data="game.remainder.waste">
</div>
</div>
\ No newline at end of file
angular.module("klondike.scoring", [])
.service("scoring", [function Scoring() {
"use strict";
this.score = 0;
this.newGame = function () {
this.score = 0;
};
this.tableauCardTurnedUp = function () {
this.score += 5;
};
this.dropped = function (source, destionation) {
this.score += scoreForMoving(source, destionation) || 0;
};
this.wasteRecycled = function () {
this.score = Math.max(this.score - 100, 0);
};
function scoreForMoving(source, destionation) {
if (destionation.name === "TableauPile") {
if (source.name === "FoundationPile") {
return -15;
}
return 5;
}
if (destionation.name === "FoundationPile") {
if (source.name === "TableauPile" || source.name === "WastePile") {
return 10;
}
}
}
}]);
{
"name": "solitaire",
"private": true,
"dependencies": {
"angular": "1.2.28",
"angular-route": "1.2.28",
"angular-loader": "1.2.28",
"angular-mocks": "1.2.28",
"underscore": "1.8.2",
"ngDraggable": "0.1.8"
}
}
version: '2'
services:
solitaire:
build: .
ports:
- 3000:80
/* eslint-env node*/
module.exports = function (config) {
"use strict";
config.set({
basePath: "./",
files: [
"app/bower_components/angular/angular.js",
"app/bower_components/angular-route/angular-route.js",
"app/bower_components/angular-mocks/angular-mocks.js",
"app/bower_components/underscore/underscore.js",
"app/cards/**/*.js",
"app/klondike/piles/pile.js",
"app/klondike/piles/foundationPile.js",
"app/klondike/piles/remainderPile.js",
"app/klondike/piles/tableauPile.js",
"app/klondike/*.js",
"tests/**/*.js"
],
autoWatch: true,
frameworks: ["jasmine"],
browsers: ["Chrome", "Firefox"],
reporters: ['progress', 'junit'],
plugins: [
"karma-chrome-launcher",
"karma-firefox-launcher",
"karma-jasmine",
"karma-junit-reporter",
"karma-phantomjs-launcher",
"karma-safari-launcher"
],
junitReporter: {
outputFile: "test-results.xml",
outputDir: "test-results"
}
});
};
{
"name": "solitaire",
"private": true,
"version": "0.0.0",
"description": "A game of solitaire",
"repository": "https://github.com/g0t4/angular-solitaire",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"bower": "^1.3.1",
"eslint": "^1.5.1",
"http-server": "^0.8.4",
"jasmine-core": "^2.3.4",
"karma": "~0.13",
"karma-chrome-launcher": "^0.2.0",
"karma-firefox-launcher": "^0.1.6",
"karma-jasmine": "^0.3.6",
"karma-junit-reporter": "^0.3.8",
"karma-phantomjs-launcher": "^1.0.1",
"karma-safari-launcher": "^1.0.0"
},
"scripts": {
"postinstall": "node node_modules/bower/bin/bower install",
"prestart": "npm install",
"start": "http-server -c-1 -o",
"pretest": "npm install",
"test": "karma start karma.conf.js",
"test-single-run": "karma start karma.conf.js --single-run",
"test-chrome": "karma start karma.conf.js --single-run --browsers Chrome"
}
}
{
"env": {
"node": true,
"jasmine": true
},
"globals": {
"module": false,
"inject": false
}
}
\ No newline at end of file
describe("A card", function () {
"use strict";
it("should link to the image of the suit and rank", function () {
expect(new Card({rank: "King", suit: "Spades"}).image).toBe("6.png");
expect(new Card({rank: "8", suit: "Hearts"}).image).toBe("27.png");
expect(new Card({rank: "Ace", suit: "Diamonds"}).image).toBe("4.png");
expect(new Card({rank: "2", suit: "Clubs"}).image).toBe("49.png");
});
it("should map its color to black or red based on suit", function () {
expect(new Card({suit: "Spades"}).color).toBe("black");
expect(new Card({suit: "Hearts"}).color).toBe("red");
expect(new Card({suit: "Diamonds"}).color).toBe("red");
expect(new Card({suit: "Clubs"}).color).toBe("black");
});
});
describe("A deck of cards", function () {
"use strict";
it("should have 52 cards", inject(function () {
expect(new Deck().unShuffled().length).toBe(52);
}));
it("should start with aces", inject(function () {
var cards = new Deck().unShuffled();
expect(cards[0].rank).toEqual("Ace");
expect(cards[0].suit).toEqual("Clubs");
expect(cards[1].rank).toEqual("Ace");
expect(cards[1].suit).toEqual("Spades");
expect(cards[2].rank).toEqual("Ace");
expect(cards[2].suit).toEqual("Hearts");
expect(cards[3].rank).toEqual("Ace");
expect(cards[3].suit).toEqual("Diamonds");
}));
it("should end with twos", inject(function () {
var cards = new Deck().unShuffled();
expect(cards[48].rank).toEqual("2");
expect(cards[48].suit).toEqual("Clubs");
expect(cards[49].rank).toEqual("2");
expect(cards[49].suit).toEqual("Spades");
expect(cards[50].rank).toEqual("2");
expect(cards[50].suit).toEqual("Hearts");
expect(cards[51].rank).toEqual("2");
expect(cards[51].suit).toEqual("Diamonds");
}));
it("should shuffle", inject(function () {
var unShuffled = new Deck().unShuffled();
var shuffled = new Deck().shuffled();
expect(shuffled).not.toEqual(unShuffled);
}));
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment