Added Software Engineering II code

This commit is contained in:
2017-11-29 12:18:13 -08:00
parent c036c6e53f
commit 4566d98b5f
54 changed files with 9288 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
CFLAGS = -Wall -fpic -coverage -lm -std=c99 -ftest-coverage -fprofile-arcs
rngs.o: rngs.h rngs.c
gcc -c rngs.c -g $(CFLAGS)
dominion.o: dominion.h dominion.c rngs.o
gcc -c dominion.c -g $(CFLAGS)
playdom: dominion.o playdom.c
gcc -o playdom playdom.c -g dominion.o rngs.o $(CFLAGS)
testDrawCard: testDrawCard.c dominion.o rngs.o
gcc -o testDrawCard -g testDrawCard.c dominion.o rngs.o $(CFLAGS)
badTestDrawCard: badTestDrawCard.c dominion.o rngs.o
gcc -o badTestDrawCard -g badTestDrawCard.c dominion.o rngs.o $(CFLAGS)
testBuyCard: testDrawCard.c dominion.o rngs.o
gcc -o testDrawCard -g testDrawCard.c dominion.o rngs.o $(CFLAGS)
testAll: dominion.o testSuite.c
gcc -o testSuite testSuite.c -g dominion.o rngs.o $(CFLAGS)
interface.o: interface.h interface.c
gcc -c interface.c -g $(CFLAGS)
runtests: testDrawCard
./testDrawCard &> unittestresult.out
gcov dominion.c >> unittestresult.out
cat dominion.c.gcov >> unittestresult.out
player: player.c interface.o
gcc -o player player.c -g dominion.o rngs.o interface.o $(CFLAGS)
all: playdom player testDrawCard testBuyCard badTestDrawCard
unittestresults.out:
echo "Compiling and running all tests....." > unittestresults.out
gcc -o unittest1 unittest1.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Unittest1...." >> unittestresults.out
./unittest1 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> unittest1.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o unittest2 unittest2.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Unittest2...." >> unittestresults.out
./unittest2 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> unittest2.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o unittest3 unittest3.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Unittest3...." >> unittestresults.out
./unittest3 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> unittest3.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o unittest4 unittest4.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Unittest4...." >> unittestresults.out
./unittest4 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> unittest4.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o cardtest1 cardtest1.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Cardtest1...." >> unittestresults.out
./cardtest1 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> cardtest1.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o cardtest2 cardtest2.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Cardtest2...." >> unittestresults.out
./cardtest2 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> cardtest2.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o cardtest3 cardtest3.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Cardtest3...." >> unittestresults.out
./cardtest3 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> cardtest3.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o cardtest4 cardtest4.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Cardtest4...." >> unittestresults.out
./cardtest4 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> cardtest4.gcov
randomtests:
gcc -o randomtestadventurer randomtestadventurer.c dominion.c rngs.c -g $(CFLAGS)
./randomtestadventurer 29378 > randomtestadventurer.out
gcov dominion.c >> randomtestadventurer.out
cat dominion.c.gcov >> randomtestadventurer.out
gcc -o randomtestcard1 randomtestcard1.c dominion.c rngs.c -g $(CFLAGS)
./randomtestcard1 29378 > randomtestcard1.out
gcov dominion.c >> randomtestcard1.out
cat dominion.c.gcov >> randomtestcard1.out
gcc -o randomtestcard2 randomtestcard2.c dominion.c rngs.c -g $(CFLAGS)
./randomtestcard2 29378 > randomtestcard2.out
gcov dominion.c >> randomtestcard2.out
cat dominion.c.gcov >> randomtestcard2.out
buggytests:
gcc -o cardtest2 cardtest2.c dominion.c rngs.c -g $(CFLAGS)
clean:
rm -f *.o playdom.exe playdom test.exe test player player.exe testInit testInit.exe *.gcov *.gcda *.gcno *.so *.out
rm -f randomtestadventurer randomtestcard1 randomtestcard2

View File

@@ -0,0 +1,44 @@
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"
#define DEBUG 0
#define NOISY_TEST 1
int checkDrawCard(int p, struct gameState *post) {
int r;
r = drawCard (p, post);
}
int main () {
int i, n, r, p, deckCount, discardCount, handCount;
int k[10] = {adventurer, council_room, feast, gardens, mine,
remodel, smithy, village, baron, great_hall};
struct gameState G;
printf ("Testing drawCard.\n");
printf ("RANDOM TESTS.\n");
SelectStream(2);
PutSeed(3);
for (n = 0; n < 2000; n++) {
for (i = 0; i < sizeof(struct gameState); i++) {
((char*)&G)[i] = floor(Random() * 256);
}
p = floor(Random() * 1000);
checkDrawCard(p, &G);
}
printf ("ALL TESTS OK\n");
exit(0);
}

View File

@@ -0,0 +1,70 @@
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"
#define DEBUG 0
#define NOISY_TEST 1
int checkDrawCard(int p, struct gameState *post) {
int r;
r = drawCard (p, post);
assert (r == 0);
}
int main () {
int i, n, r, p, deckCount, discardCount, handCount;
int k[10] = {adventurer, council_room, feast, gardens, mine,
remodel, smithy, village, baron, great_hall};
struct gameState G;
printf ("Testing drawCard.\n");
printf ("RANDOM TESTS.\n");
SelectStream(2);
PutSeed(3);
for (n = 0; n < 2000; n++) {
for (i = 0; i < sizeof(struct gameState); i++) {
((char*)&G)[i] = floor(Random() * 256);
}
p = floor(Random() * 2);
G.deckCount[p] = floor(Random() * MAX_DECK);
G.discardCount[p] = floor(Random() * MAX_DECK);
G.handCount[p] = floor(Random() * MAX_HAND);
checkDrawCard(p, &G);
}
printf ("ALL TESTS OK\n");
exit(0);
printf ("SIMPLE FIXED TESTS.\n");
for (p = 0; p < 2; p++) {
for (deckCount = 0; deckCount < 5; deckCount++) {
for (discardCount = 0; discardCount < 5; discardCount++) {
for (handCount = 0; handCount < 5; handCount++) {
memset(&G, 23, sizeof(struct gameState));
r = initializeGame(2, k, 1, &G);
G.deckCount[p] = deckCount;
memset(G.deck[p], 0, sizeof(int) * deckCount);
G.discardCount[p] = discardCount;
memset(G.discard[p], 0, sizeof(int) * discardCount);
G.handCount[p] = handCount;
memset(G.hand[p], 0, sizeof(int) * handCount);
checkDrawCard(p, &G);
}
}
}
}
return 0;
}

View File

@@ -0,0 +1,74 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int arbitrary_hand_count_max = MAX_HAND - 1;
for(int i = 0 ; i < 4 ; i++){
initializeGame(4, k, 65432, &G);
G.deckCount[i] = MAX_DECK;
G.whoseTurn = i;
for(int j = 0 ; j < arbitrary_hand_count_max ; j++){
int hand_count = G.handCount[i];
int num_actions = G.numActions;
playGreat_Hall(&G, j);
testing_assert(G.handCount[i] == hand_count);
testing_assert(G.numActions == num_actions + 1);
for(int j = 0 ; j < G.handCount[i] ; j++){
testing_assert((G.hand[i][j] >= curse) && (G.hand[i][j] <= treasure_map));
}
}
}
// printf("Hand: %d\n", G.handCount[0]);
// drawCard(0, &G);
// printf("Hand: %d\n", G.handCount[0]);
// G.numActions++;
// discardCard(0, 0, &G, 0);
// printf("Hand: %d\n\n", G.handCount[0]);
//
// printf("Hand: %d\n", G.handCount[0]);
// playGreat_Hall(&G, 0);
// printf("Hand: %d\n\n", G.handCount[0]);
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,85 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression, int should_print);
long sucesses = 0;
long failures = 0;
int main(int argc, char** argv) {
struct gameState G, G_copy;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int arbitrary_hand_count_max = MAX_HAND - 3;
for(int l = 0 ; l < 4 ; l++){
for(int i = 1 ; i < arbitrary_hand_count_max ; i++){
initializeGame(4, k, 65432, &G);
G.handCount[0] = arbitrary_hand_count_max;
G.whoseTurn = l;
G_copy = G;
drawCard(0, &G_copy);
drawCard(0, &G_copy);
drawCard(0, &G_copy);
discardCard(i, l, &G_copy, 0);
playSmithy(&G, i);
testing_assert(G.handCount[l] == G_copy.handCount[l], 0);
for(int j = 0 ; j < arbitrary_hand_count_max ; j++){
testing_assert((G.hand[l][j] >= curse) && (G.hand[l][j] <= treasure_map), 0);
testing_assert((G_copy.hand[l][j] >= curse) && (G_copy.hand[l][j] <= treasure_map), 0);
}
}
}
printf("Run complete!\n");
printf("SUCCESSES: %ld\n", sucesses);
if(failures > 0){
printf("Some tests failed!!!\n");
printf("FAILURES: %ld\n", failures);
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression, int should_print) {
if (expression) {
if(should_print){
printf("TEST SUCCEEDED!\n");
}
sucesses++;
return 1;
} else {
if(should_print){
printf("TEST FAILED!\n");
}
failures++;
return 0;
}
}

View File

@@ -0,0 +1,77 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int arbitrary_hand_count_max = MAX_HAND - 2;
for(int l = 0 ; l < 4 ; l++){
for(int i = 1 ; i < arbitrary_hand_count_max ; i++){
initializeGame(4, k, 65432, &G);
G.handCount[l] = arbitrary_hand_count_max;
G.whoseTurn = l;
playAdventurer(&G);
testing_assert(G.handCount[l] == arbitrary_hand_count_max + 2);
testing_assert((G.hand[l][G.handCount[l] - 1] >= copper) && (G.hand[l][G.handCount[l] - 1] <= gold));
testing_assert((G.hand[l][G.handCount[l] - 2] >= copper) && (G.hand[l][G.handCount[l] - 2] <= gold));
initializeGame(4, k, 65432, &G);
G.handCount[l] = arbitrary_hand_count_max;
G.whoseTurn = l;
G.deckCount[l] = 0;
for(int m = 0 ; m < MAX_DECK ; m += 3){
G.discard[l][m] = copper;
G.discard[l][m + 1] = silver;
G.discard[l][m + 2] = gold;
G.discardCount[l] += 3;
}
playAdventurer(&G);
testing_assert(G.handCount[l] == arbitrary_hand_count_max + 2);
testing_assert((G.hand[l][G.handCount[l] - 1] >= copper) && (G.hand[l][G.handCount[l] - 1] <= gold));
testing_assert((G.hand[l][G.handCount[l] - 2] >= copper) && (G.hand[l][G.handCount[l] - 2] <= gold));
}
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,57 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int arbitrary_hand_count_max = MAX_HAND - 3;
for(int l = 0 ; l < 4 ; l++){
for(int i = 1 ; i < arbitrary_hand_count_max ; i++){
initializeGame(4, k, 65432, &G);
G.handCount[0] = arbitrary_hand_count_max;
G.whoseTurn = l;
int outpost_before = G.outpostPlayed;
int handcount_before = G.handCount[l];
playOutpost(&G, i);
testing_assert(G.outpostPlayed == outpost_before + 1);
testing_assert(G.handCount[l] == handcount_before - 1);
}
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,142 @@
#ifndef _DOMINION_H
#define _DOMINION_H
// Code from various sources, baseline from Kristen Bartosz
#define MAX_HAND 500
#define MAX_DECK 500
#define MAX_PLAYERS 4
#define DEBUG 0
//Testing commit
/* http://dominion.diehrstraits.com has card texts */
/* http://dominion.isotropic.org has other stuff */
/* hand# means index of a card in current active player's hand */
enum CARD
{curse = 0,
estate,
duchy,
province,
copper,
silver,
gold,
adventurer,
/* If no/only 1 treasure found, stop when full deck seen */
council_room,
feast, /* choice1 is supply # of card gained) */
gardens,
mine, /* choice1 is hand# of money to trash, choice2 is supply# of
money to put in hand */
remodel, /* choice1 is hand# of card to remodel, choice2 is supply# */
smithy,
village,
baron, /* choice1: boolean for discard of estate */
/* Discard is always of first (lowest index) estate */
great_hall,
minion, /* choice1: 1 = +2 coin, 2 = redraw */
steward, /* choice1: 1 = +2 card, 2 = +2 coin, 3 = trash 2 (choice2,3) */
tribute,
ambassador, /* choice1 = hand#, choice2 = number to return to supply */
cutpurse,
embargo, /* choice1 = supply# */
outpost,
salvager, /* choice1 = hand# to trash */
sea_hag,
treasure_map
};
struct gameState {
int numPlayers; //number of players
int supplyCount[treasure_map+1]; //this is the amount of a specific type of card given a specific number.
int embargoTokens[treasure_map+1];
int outpostPlayed;
int outpostTurn;
int whoseTurn;
int phase;
int numActions; /* Starts at 1 each turn */
int coins; /* Use as you see fit! */
int numBuys; /* Starts at 1 each turn */
int hand[MAX_PLAYERS][MAX_HAND];
int handCount[MAX_PLAYERS];
int deck[MAX_PLAYERS][MAX_DECK];
int deckCount[MAX_PLAYERS];
int discard[MAX_PLAYERS][MAX_DECK];
int discardCount[MAX_PLAYERS];
int playedCards[MAX_DECK];
int playedCardCount;
};
/* All functions return -1 on failure, and DO NOT CHANGE GAME STATE;
unless specified for other return, return 0 on success */
struct gameState* newGame();
int* kingdomCards(int k1, int k2, int k3, int k4, int k5, int k6, int k7,
int k8, int k9, int k10);
int initializeGame(int numPlayers, int kingdomCards[10], int randomSeed,
struct gameState *state);
/* Responsible for initializing all supplies, and shuffling deck and
drawing starting hands for all players. Check that 10 cards selected
are in fact (different) kingdom cards, and that numPlayers is valid.
Cards not in game should initialize supply position to -1 */
int shuffle(int player, struct gameState *state);
/* Assumes all cards are now in deck array (or hand/played): discard is
empty */
int playCard(int handPos, int choice1, int choice2, int choice3,
struct gameState *state);
/* Play card with index handPos from current player's hand */
int buyCard(int supplyPos, struct gameState *state);
/* Buy card with supply index supplyPos */
int numHandCards(struct gameState *state);
/* How many cards current player has in hand */
int handCard(int handNum, struct gameState *state);
/* enum value of indexed card in player's hand */
int supplyCount(int card, struct gameState *state);
/* How many of given card are left in supply */
int fullDeckCount(int player, int card, struct gameState *state);
/* Here deck = hand + discard + deck */
int whoseTurn(struct gameState *state);
int endTurn(struct gameState *state);
/* Must do phase C and advance to next player; do not advance whose turn
if game is over */
int isGameOver(struct gameState *state);
int scoreFor(int player, struct gameState *state);
/* Negative here does not mean invalid; scores may be negative,
-9999 means invalid input */
int getWinners(int players[MAX_PLAYERS], struct gameState *state);
/* Set array position of each player who won (remember ties!) to
1, others to 0 */
int playAdventurer(struct gameState *state);
int playSmithy(struct gameState *state, int handPos);
int playVillage(struct gameState *state, int handPos);
int playFeast(struct gameState *state, int choice1);
int playCouncil_Room(struct gameState *state, int handPos);
int playGreat_Hall(struct gameState *state, int handPos);
int playOutpost(struct gameState *state, int handPos);
#endif

View File

@@ -0,0 +1,15 @@
#ifndef _DOMINION_HELPERS_H
#define _DOMINION_HELPERS_H
#include "dominion.h"
int drawCard(int player, struct gameState *state);
int updateCoins(int player, struct gameState *state, int bonus);
int discardCard(int handPos, int currentPlayer, struct gameState *state,
int trashFlag);
int gainCard(int supplyPos, struct gameState *state, int toFlag, int player);
int getCost(int cardNumber);
int cardEffect(int card, int choice1, int choice2, int choice3,
struct gameState *state, int handPos, int *bonus);
#endif

View File

@@ -0,0 +1,363 @@
/* Interactive Dominion Interface
Sam Heinith CS362
1/26/2010
*/
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include "rngs.h"
#include "interface.h"
#include "dominion.h"
void cardNumToName(int card, char *name){
switch(card){
case curse: strcpy(name,"Curse");
break;
case estate: strcpy(name,"Estate");
break;
case duchy: strcpy(name,"Duchy");
break;
case province: strcpy(name,"Province");
break;
case copper: strcpy(name,"Copper");
break;
case silver: strcpy(name,"Silver");
break;
case gold: strcpy(name,"Gold");
break;
case adventurer: strcpy(name,"Adventurer");
break;
case council_room: strcpy(name,"Council Room");
break;
case feast: strcpy(name,"Feast");
break;
case gardens: strcpy(name,"Gardens");
break;
case mine: strcpy(name,"Mine");
break;
case remodel: strcpy(name,"Remodel");
break;
case smithy: strcpy(name,"Smithy");
break;
case village: strcpy(name,"Village");
break;
case baron: strcpy(name,"Baron");
break;
case great_hall: strcpy(name,"Great Hall");
break;
case minion: strcpy(name,"Minion");
break;
case steward: strcpy(name,"Steward");
break;
case tribute: strcpy(name,"Tribute");
break;
case ambassador: strcpy(name,"Ambassador");
break;
case cutpurse: strcpy(name,"Cutpurse");
break;
case embargo: strcpy(name,"Embargo");
break;
case outpost: strcpy(name,"Outpost");
break;
case salvager: strcpy(name,"Salvager");
break;
case sea_hag: strcpy(name,"Sea Hag");
break;
case treasure_map: strcpy(name,"Treasure Map");
break;
default: strcpy(name,"?");
}
}
int getCardCost(int card) {
int cost;
switch(card) {
case curse: cost = CURSE_COST;
break;
case estate: cost = ESTATE_COST;
break;
case duchy: cost = DUCHY_COST;
break;
case province: cost = PROVINCE_COST;
break;
case copper: cost = COPPER_COST;
break;
case silver: cost = SILVER_COST;
break;
case gold: cost = GOLD_COST;
break;
case adventurer: cost = ADVENTURER_COST;
break;
case council_room: cost = COUNCIL_ROOM_COST;
break;
case feast: cost = FEAST_COST;
break;
case gardens: cost = GARDEN_COST;
break;
case mine: cost = MINE_COST;
break;
case remodel: cost = REMODEL_COST;
break;
case smithy: cost = SMITHY_COST;
break;
case village: cost = VILLAGE_COST;
break;
case baron: cost = BARON_COST;
break;
case great_hall: cost = GREAT_HALL_COST;
break;
case minion: cost = MINION_COST;
break;
case steward: cost = STEWARD_COST;
break;
case tribute: cost = TRIBUTE_COST;
break;
case ambassador: cost = AMBASSADOR_COST;
break;
case cutpurse: cost = CUTPURSE_COST;
break;
case embargo: cost = EMBARGO_COST;
break;
case outpost: cost = OUTPOST_COST;
break;
case salvager: cost = SALVAGER_COST;
break;
case sea_hag: cost = SEA_HAG_COST;
break;
case treasure_map: cost = TREASURE_MAP_COST;
break;
default: cost = ONETHOUSAND;
}
return cost;
}
void printHand(int player, struct gameState *game) {
int handCount = game->handCount[player];
int handIndex;
printf("Player %d's hand:\n", player);
if(handCount > 0) printf("# Card\n");
for(handIndex = 0; handIndex < handCount; handIndex++) {
int card = game->hand[player][handIndex];
char name[MAX_STRING_LENGTH];
cardNumToName(card, name);
printf("%-2d %-13s\n", handIndex, name);
}
printf("\n");
}
void printDeck(int player, struct gameState *game) {
int deckCount = game->deckCount[player];
int deckIndex;
printf("Player %d's deck: \n", player);
if(deckCount > 0) printf("# Card\n");
for(deckIndex = 0; deckIndex < deckCount; deckIndex++) {
int card = game->deck[player][deckIndex];
char name[MAX_STRING_LENGTH];
cardNumToName(card, name);
printf("%-2d %-13s\n", deckIndex, name);
}
printf("\n");
}
void printPlayed(int player, struct gameState *game) {
int playedCount = game->playedCardCount;
int playedIndex;
printf("Player %d's played cards: \n", player);
if(playedCount > 0) printf("# Card\n");
for(playedIndex = 0; playedIndex < playedCount; playedIndex++) {
int card = game->playedCards[playedIndex];
char name[MAX_STRING_LENGTH];
cardNumToName(card, name);
printf("%-2d %-13s \n", playedIndex, name);
}
printf("\n");
}
void printDiscard(int player, struct gameState *game) {
int discardCount = game->discardCount[player];
int discardIndex;
printf("Player %d's discard: \n", player);
if(discardCount > 0) printf("# Card\n");
for(discardIndex = 0; discardIndex < discardCount; discardIndex++) {
int card = game->discard[player][discardIndex];
char name[MAX_STRING_LENGTH];
cardNumToName(card, name);
printf("%-2d %-13s \n", discardIndex, name);
}
printf("\n");
}
void printSupply(struct gameState *game) {
int cardNum, cardCost, cardCount;
char name[MAX_STRING_LENGTH];
printf("# Card Cost Copies\n");
for(cardNum = 0; cardNum < NUM_TOTAL_K_CARDS; cardNum++){
cardCount = game->supplyCount[cardNum];
if(cardCount == -1) continue;
cardNumToName(cardNum, name);
cardCost = getCardCost(cardNum);
printf("%-2d %-13s %-5d %-5d", cardNum, name, cardCost, cardCount);
printf("\n");
}
printf("\n");
}
void printState(struct gameState *game) {
int numActions = game->numActions;
int numCoins = game->coins;
int numBuys = game->numBuys;
int currentPlayer = game->whoseTurn;
int phase = game->phase;
char phaseName[MAX_STRING_LENGTH];
phaseNumToName(phase,phaseName);
printf("Player %d:\n%s phase\n%d actions\n%d coins\n%d buys\n\n", currentPlayer, phaseName, numActions, numCoins, numBuys);
}
void printScores(struct gameState *game) {
int playerNum, score[MAX_PLAYERS];
int numPlayers = game->numPlayers;
for(playerNum = 0; playerNum < numPlayers; playerNum++) {
score[playerNum] = scoreFor(playerNum,game);
printf("Player %d has a score of %d\n", playerNum, score[playerNum]);
}
}
void printHelp(void) {
printf("Commands are: \n\
add [Supply Card Number] - add any card to your hand (teh hacks)\n\
buy [Supply Card Number] - buy a card at supply position\n\
end - end your turn\n\
init [Number of Players] [Number of Bots] - initialize the game\n\
num - print number of cards in your hand\n\
play [Hand Index] [Choice] [Choice] [Choice] - play a card from your hand\n\
resign - end the game showing the current scores\n\
show - show your current hand\n\
stat - show your turn's status\n\
supp - show the supply\n\
whos - whos turn\n\
exit - exit the interface");
printf("\n\n");
}
void phaseNumToName(int phase, char *name) {
switch(phase){
case ACTION_PHASE: strcpy(name,"Action");
break;
case BUY_PHASE: strcpy(name,"Buy");
break;
case CLEANUP_PHASE: strcpy(name,"Cleanup");
break;
}
}
int addCardToHand(int player, int card, struct gameState *game) {
if(card >= adventurer && card < NUM_TOTAL_K_CARDS){
int handTop = game->handCount[player];
game->hand[player][handTop] = card;
game->handCount[player]++;
return SUCCESS;
} else {
return FAILURE;
}
}
void selectKingdomCards(int randomSeed, int kingCards[NUM_K_CARDS]) {
int i, used, card, numSelected = 0;
SelectStream(1);
PutSeed((long)randomSeed);
while(numSelected < NUM_K_CARDS) {
used = FALSE;
card = floor(Random() * NUM_TOTAL_K_CARDS);
if(card < adventurer) continue;
for(i = 0; i < numSelected; i++) {
if(kingCards[i] == card) {
used = TRUE;
break;
}
}
if(used == TRUE) continue;
kingCards[numSelected] = card;
numSelected++;
}
}
int countHandCoins(int player, struct gameState *game) {
int card, index, coinage = 0;
for(index = 0; index < game->handCount[player]; index++) {
card = game->hand[player][index];
switch(card) {
case copper: coinage += COPPER_VALUE;
break;
case silver: coinage += SILVER_VALUE;
break;
case gold: coinage += GOLD_VALUE;
break;
}
}
return coinage;
}
void executeBotTurn(int player, int *turnNum, struct gameState *game) {
int coins = countHandCoins(player, game);
printf("*****************Executing Bot Player %d Turn Number %d*****************\n", player, *turnNum);
printSupply(game);
//sleep(1); //Thinking...
if(coins >= PROVINCE_COST && supplyCount(province,game) > 0) {
buyCard(province,game);
printf("Player %d buys card Province\n\n", player);
}
else if(supplyCount(province,game) == 0 && coins >= DUCHY_COST ) {
buyCard(duchy,game);
printf("Player %d buys card Duchy\n\n", player);
}
else if(coins >= GOLD_COST && supplyCount(gold,game) > 0) {
buyCard(gold,game);
printf("Player %d buys card Gold\n\n", player);
}
else if(coins >= SILVER_COST && supplyCount(silver,game) > 0) {
buyCard(silver,game);
printf("Player %d buys card Silver\n\n", player);
}
if(player == (game->numPlayers -1)) (*turnNum)++;
endTurn(game);
if(! isGameOver(game)) {
int currentPlayer = whoseTurn(game);
printf("Player %d's turn number %d\n\n", currentPlayer, (*turnNum));
}
}

View File

@@ -0,0 +1,128 @@
/* Interactive Dominion Interface
Sam Heinith CS362
1/26/2010
*/
#ifndef _INTERFACE_H
#define _INTERFACE_H
#include "dominion.h"
//Last card enum (Treasure map) card number plus one for the 0th card.
#define NUM_TOTAL_K_CARDS (treasure_map + 1)
#define NUM_K_CARDS 10
#define NUM_V_CARDS_2 8
#define NUM_V_CARDS_3or4 12
#define NUM_C_CARDS_2 10
#define NUM_C_CARDS_3 20
#define NUM_C_CARDS_4 30
#define NUM_COPPER 60
#define NUM_SILVER 40
#define NUM_GOLD 30
#define UNUSED -1
#define START_COPPER 7
#define START_ESTATE 3
#define HANDSIZE 5
#define COMPARE(string1, string2) strncmp(string1, string2, 4)
#define MAX_STRING_LENGTH 32
#define TRUE 1
#define FALSE 0
#define SUCCESS 0
#define FAILURE -1
#define MATCH 0
#define WINNER 1
#define NOT_WINNER 0
//The Game Phases
#define ACTION_PHASE 0
#define BUY_PHASE 1
#define CLEANUP_PHASE 2
#define COPPER_VALUE 1
#define SILVER_VALUE 2
#define GOLD_VALUE 3
//From Dominion List Spoiler
#define COPPER_COST 0
#define SILVER_COST 3
#define GOLD_COST 6
#define ESTATE_COST 2
#define DUCHY_COST 5
#define PROVINCE_COST 8
#define CURSE_COST 0
#define ADVENTURER_COST 6
#define COUNCIL_ROOM_COST 5
#define FEAST_COST 4
#define GARDEN_COST 4
#define MINE_COST 5
#define MONEYLENDER_COST 4
#define REMODEL_COST 4
#define SMITHY_COST 4
#define VILLAGE_COST 3
#define WOODCUTTER_COST 3
#define BARON_COST 4
#define GREAT_HALL_COST 3
#define MINION_COST 5
#define SHANTY_TOWN_COST 3
#define STEWARD_COST 3
#define TRIBUTE_COST 5
#define WISHING_WELL_COST 3
#define AMBASSADOR_COST 3
#define CUTPURSE_COST 4
#define EMBARGO_COST 2
#define OUTPOST_COST 5
#define SALVAGER_COST 4
#define SEA_HAG_COST 4
#define TREASURE_MAP_COST 4
#define ONETHOUSAND 1000
int addCardToHand(int player, int card, struct gameState *game);
int countHandCoins(int player, struct gameState *game);
void executeBotTurn(int player, int *turnNum, struct gameState *game);
void phaseNumToName(int phase, char *name);
void cardNumToName(int card, char *name);
int getCardCost(int card);
void printHelp(void);
void printHand(int player, struct gameState *game);
void printDeck(int player, struct gameState *game);
void printDiscard(int player, struct gameState *game);
void printPlayed(int player, struct gameState *game);
void printState(struct gameState *game);
void printSupply(struct gameState *game);
void printGameState(struct gameState *game);
void printScores(struct gameState *game);
void selectKingdomCards(int randomSeed, int kingdomCards[NUM_K_CARDS]);
#endif

View File

@@ -0,0 +1,134 @@
#include "dominion.h"
#include <stdio.h>
#include "rngs.h"
#include <stdlib.h>
int main (int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
printf ("Starting game.\n");
initializeGame(2, k, atoi(argv[1]), &G);
int money = 0;
int smithyPos = -1;
int adventurerPos = -1;
int i=0;
int numSmithies = 0;
int numAdventurers = 0;
while (!isGameOver(&G)) {
money = 0;
smithyPos = -1;
adventurerPos = -1;
for (i = 0; i < numHandCards(&G); i++) {
if (handCard(i, &G) == copper)
money++;
else if (handCard(i, &G) == silver)
money += 2;
else if (handCard(i, &G) == gold)
money += 3;
else if (handCard(i, &G) == smithy)
smithyPos = i;
else if (handCard(i, &G) == adventurer)
adventurerPos = i;
}
if (whoseTurn(&G) == 0) {
if (smithyPos != -1) {
printf("0: smithy played from position %d\n", smithyPos);
playCard(smithyPos, -1, -1, -1, &G);
printf("smithy played.\n");
money = 0;
i=0;
while(i<numHandCards(&G)){
if (handCard(i, &G) == copper){
playCard(i, -1, -1, -1, &G);
money++;
}
else if (handCard(i, &G) == silver){
playCard(i, -1, -1, -1, &G);
money += 2;
}
else if (handCard(i, &G) == gold){
playCard(i, -1, -1, -1, &G);
money += 3;
}
i++;
}
}
if (money >= 8) {
printf("0: bought province\n");
buyCard(province, &G);
}
else if (money >= 6) {
printf("0: bought gold\n");
buyCard(gold, &G);
}
else if ((money >= 4) && (numSmithies < 2)) {
printf("0: bought smithy\n");
buyCard(smithy, &G);
numSmithies++;
}
else if (money >= 3) {
printf("0: bought silver\n");
buyCard(silver, &G);
}
printf("0: end turn\n");
endTurn(&G);
}
else {
if (adventurerPos != -1) {
printf("1: adventurer played from position %d\n", adventurerPos);
playCard(adventurerPos, -1, -1, -1, &G);
money = 0;
i=0;
while(i<numHandCards(&G)){
if (handCard(i, &G) == copper){
playCard(i, -1, -1, -1, &G);
money++;
}
else if (handCard(i, &G) == silver){
playCard(i, -1, -1, -1, &G);
money += 2;
}
else if (handCard(i, &G) == gold){
playCard(i, -1, -1, -1, &G);
money += 3;
}
i++;
}
}
if (money >= 8) {
printf("1: bought province\n");
buyCard(province, &G);
}
else if ((money >= 6) && (numAdventurers < 2)) {
printf("1: bought adventurer\n");
buyCard(adventurer, &G);
numAdventurers++;
}else if (money >= 6){
printf("1: bought gold\n");
buyCard(gold, &G);
}
else if (money >= 3){
printf("1: bought silver\n");
buyCard(silver, &G);
}
printf("1: endTurn\n");
endTurn(&G);
}
} // end of While
printf ("Finished game.\n");
printf ("Player 0: %d\nPlayer 1: %d\n", scoreFor(0, &G), scoreFor(1, &G));
return 0;
}

View File

@@ -0,0 +1,216 @@
/* Interactive Dominion Interface
Version 7
Sam Heinith CS362
Questions/Comments:
heiniths@onid.orst.edu
1/26/2010
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "dominion.h"
#include "interface.h"
#include "rngs.h"
int main2(int argc, char *argv[]) {
//Default cards, as defined in playDom
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse, sea_hag, tribute, smithy};
struct gameState g;
initializeGame(2,k,1,&g);
printf ("SUCCESSFUL INIT\n");
getchar();
}
int main(int argc, char* argv[]) {
char *add = "add";
char *buyC = "buy";
char *endT = "end";
char *exit = "exit";
char *help = "help";
char *init = "init";
char *numH = "num";
char *play = "play";
char *resign = "resi";
char *show = "show";
char *stat = "stat";
char *supply = "supp";
char *whos = "whos";
char command[MAX_STRING_LENGTH];
char line[MAX_STRING_LENGTH];
char cardName[MAX_STRING_LENGTH];
//Array to hold bot presence
int isBot[MAX_PLAYERS] = { 0, 0, 0, 0};
int players[MAX_PLAYERS];
int playerNum;
int outcome;
int currentPlayer;
int gameOver = FALSE;
int gameStarted = FALSE;
int turnNum = 0;
int randomSeed = atoi(argv[1]);
//Default cards, as defined in playDom
int kCards[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse, sea_hag, tribute, smithy};
struct gameState g;
struct gameState * game = &g;
memset(game,0,sizeof(struct gameState));
if(argc != 2){
printf("Usage: player [integer random number seed]\n");
return EXIT_SUCCESS;
}
if(randomSeed <= 0){
printf("Usage: player [integer random number seed]\n");
return EXIT_SUCCESS;
}
initializeGame(2,kCards,randomSeed,game);
printf("Please enter a command or \"help\" for commands\n");
while(TRUE) {
int arg0 = UNUSED;
int arg1 = UNUSED;
int arg2 = UNUSED;
int arg3 = UNUSED;
outcome = FAILURE;
strcpy(line,"");
strcpy(command,"");
strcpy(cardName,"");
currentPlayer = whoseTurn(game);
//If you are getting a seg fault comment this if block out
gameOver = isGameOver(game);
if(gameStarted == TRUE && gameOver == TRUE){
printScores(game);
getWinners(players, game);
printf("After %d turns, the winner(s) are:\n", turnNum);
for(playerNum = 0; playerNum < game->numPlayers; playerNum++){
if(players[playerNum] == WINNER) printf("Player %d\n", playerNum);
}
for(playerNum = 0; playerNum < game->numPlayers; playerNum++){
printHand(playerNum, game);
printPlayed(playerNum, game);
printDiscard(playerNum, game);
printDeck(playerNum, game);
}
break; //Exit out of the game/while loop
}
if(isBot[currentPlayer] == TRUE) {
executeBotTurn(currentPlayer, &turnNum, game);
continue;
}
printf("$ ");
fgets(line, MAX_STRING_LENGTH, stdin);
sscanf(line, "%s %d %d %d %d", command, &arg0, &arg1, &arg2, &arg3);
if(COMPARE(command, add) == 0) {
outcome = addCardToHand(currentPlayer, arg0, game);
cardNumToName(arg0, cardName);
printf("Player %d adds %s to their hand\n\n", currentPlayer, cardName);
} else
if(COMPARE(command, buyC) == 0) {
outcome = buyCard(arg0, game);
cardNumToName(arg0, cardName);
if(outcome == SUCCESS){
printf("Player %d buys card %d, %s\n\n", currentPlayer, arg0, cardName);
} else {
printf("Player %d cannot buy card %d, %s\n\n", currentPlayer, arg0, cardName);
}
} else
if(COMPARE(command, endT) == 0) {
if(gameStarted == TRUE) {
if(currentPlayer == (game->numPlayers -1)) turnNum++;
endTurn(game);
currentPlayer = whoseTurn(game);
printf("Player %d's turn number %d\n\n", currentPlayer, turnNum);
}
} else
if(COMPARE(command, exit) == 0) {
break;
} else
if(COMPARE(command, help) == 0) {
printHelp();
} else
if(COMPARE(command, init) == 0) {
int numHuman = arg0 - arg1;
for(playerNum = numHuman; playerNum < arg0; playerNum++) {
isBot[playerNum] = TRUE;
}
// selectKingdomCards(randomSeed, kCards); //Comment this out to use the default card set defined in playDom.
outcome = initializeGame(arg0, kCards, randomSeed, game);
printf("\n");
if(outcome == SUCCESS){
gameStarted = TRUE;
currentPlayer = whoseTurn(game);
printf("Player %d's turn number %d\n\n", currentPlayer, turnNum);
}
} else
if(COMPARE(command, numH) == 0) {
int numCards = numHandCards(game);
printf("There are %d cards in your hand.\n", numCards);
} else
if(COMPARE(command, play) == 0) {
int card = handCard(arg0,game);
outcome = playCard(arg0, arg1, arg2, arg3, game);
cardNumToName(card, cardName);
if(outcome == SUCCESS){
printf("Player %d plays %s\n\n", currentPlayer, cardName);
} else {
printf("Player %d cannot play card %d\n\n", currentPlayer, arg0);
}
} else
if(COMPARE(command, resign) == 0) {
endTurn(game);
printScores(game);
break;
} else
if(COMPARE(command, show) == 0) {
if(gameStarted == FALSE) continue;
printHand(currentPlayer, game);
printPlayed(currentPlayer, game);
//printDiscard(currentPlayer, game);
//printDeck(currentPlayer, game);
} else
if(COMPARE(command, stat) == 0) {
if(gameStarted == FALSE) continue;
printState(game);
} else
if(COMPARE(command, supply) == 0) {
printSupply(game);
} else
if(COMPARE(command, whos) == 0) {
int playerNum = whoseTurn(game);
printf("Player %d's turn\n", playerNum);
}
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,100 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression, int should_print);
long sucesses = 0;
long failures = 0;
int main(int argc, char** argv) {
// Get seed from command line arg and setup randomization or exit
if(argc < 2){
printf("Please enter a seed value for randomization.\n");
printf("usage: %s [seed]\n", argv[0]);
return EXIT_FAILURE;
}
long seed = atoi(argv[1]);
SelectStream(1);
PutSeed(seed);
// Game variables
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
// Initialize the game
initializeGame(4, k, seed, &G);
// Limiting variables
unsigned int arbritray_num_tests = 100000;
unsigned int arbitrary_hand_count_max = MAX_HAND - 2;
for(int z = 0 ; z < arbritray_num_tests ; z++){
int turn = floor(Random() *MAX_PLAYERS);
int starting_hand = floor(Random() * arbitrary_hand_count_max);
G.deckCount[turn] = MAX_DECK;
G.discardCount[turn] = 0;
G.handCount[turn] = starting_hand;
G.whoseTurn = turn;
playAdventurer(&G);
testing_assert(G.handCount[turn] == starting_hand + 2, 0);
testing_assert((G.hand[turn][G.handCount[turn] - 1] >= copper) && (G.hand[turn][G.handCount[turn] - 1] <= gold), 0);
testing_assert((G.hand[turn][G.handCount[turn] - 2] >= copper) && (G.hand[turn][G.handCount[turn] - 2] <= gold), 0);
G.handCount[turn] = starting_hand;
G.deckCount[turn] = 0;
G.discardCount[turn] = 0;
for(int m = 0 ; m < MAX_DECK ; m++){
int card = copper + floor(Random() * 2);
G.discard[turn][m] = card;
G.discardCount[turn]++;
}
playAdventurer(&G);
testing_assert(G.handCount[turn] == starting_hand + 2, 0);
testing_assert((G.hand[turn][G.handCount[turn] - 1] >= copper) && (G.hand[turn][G.handCount[turn] - 1] <= gold), 0);
testing_assert((G.hand[turn][G.handCount[turn] - 2] >= copper) && (G.hand[turn][G.handCount[turn] - 2] <= gold), 0);
}
printf("Run complete!\n");
printf("SUCCESSES: %ld\n", sucesses);
if(failures > 0){
printf("Some tests failed!!!\n");
printf("FAILURES: %ld\n", failures);
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression, int should_print) {
if (expression) {
if(should_print){
printf("TEST SUCCEEDED!\n");
}
sucesses++;
return 1;
} else {
if(should_print){
printf("TEST FAILED!\n");
}
failures++;
return 0;
}
}

View File

@@ -0,0 +1,78 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression, int should_print);
long sucesses = 0;
long failures = 0;
int main(int argc, char** argv) {
// Get seed from command line arg and setup randomization or exit
if(argc < 2){
printf("Please enter a seed value for randomization.\n");
printf("usage: %s [seed]\n", argv[0]);
return EXIT_FAILURE;
}
long seed = atoi(argv[1]);
SelectStream(1);
PutSeed(seed);
// Game variables
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
// Initialize the game
initializeGame(4, k, seed, &G);
// Limiting variables
unsigned int arbritray_num_tests = 1000000;
unsigned int arbitrary_hand_count_max = MAX_HAND - 2;
for(int z = 0 ; z < arbritray_num_tests ; z++){
int turn = floor(Random() * MAX_PLAYERS);
int starting_hand = floor(Random() * arbitrary_hand_count_max);
G.handCount[turn] = starting_hand;
G.whoseTurn = turn;
int outpost_before = G.outpostPlayed;
int handcount_before = G.handCount[turn];
playOutpost(&G, turn);
testing_assert(G.outpostPlayed == outpost_before + 1, 0);
testing_assert(G.handCount[turn] == handcount_before - 1, 0);
}
printf("Run complete!\n");
printf("SUCCESSES: %ld\n", sucesses);
if(failures > 0){
printf("Some tests failed!!!\n");
printf("FAILURES: %ld\n", failures);
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression, int should_print) {
if (expression) {
if(should_print){
printf("TEST SUCCEEDED!\n");
}
sucesses++;
return 1;
} else {
if(should_print){
printf("TEST FAILED!\n");
}
failures++;
return 0;
}
}

View File

@@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression, int should_print);
long sucesses = 0;
long failures = 0;
int main(int argc, char** argv) {
// Get seed from command line arg and setup randomization or exit
if(argc < 2){
printf("Please enter a seed value for randomization.\n");
printf("usage: %s [seed]\n", argv[0]);
return EXIT_FAILURE;
}
long seed = atoi(argv[1]);
SelectStream(1);
PutSeed(seed);
// Game variables
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
// Initialize the game
initializeGame(4, k, seed, &G);
// Limiting variables
unsigned int arbritray_num_tests = 1000000;
unsigned int arbitrary_hand_count_max = MAX_HAND - 2;
for(int z = 0 ; z < arbritray_num_tests ; z++){
int turn = floor(Random() * MAX_PLAYERS);
int hand_pos = floor(Random() * arbitrary_hand_count_max);
G.deckCount[turn] = MAX_DECK;
G.whoseTurn = turn;
int hand_count = G.handCount[turn];
int num_actions = G.numActions;
playGreat_Hall(&G, hand_pos);
testing_assert(G.handCount[turn] == hand_count, 0);
testing_assert(G.numActions == num_actions + 1, 0);
for(int k = 0 ; k < G.handCount[turn] ; k++){
testing_assert((G.hand[turn][k] >= curse) && (G.hand[turn][k] <= treasure_map), 0);
}
}
printf("Run complete!\n");
printf("SUCCESSES: %ld\n", sucesses);
if(failures > 0){
printf("Some tests failed!!!\n");
printf("FAILURES: %ld\n", failures);
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression, int should_print) {
if (expression) {
if(should_print){
printf("TEST SUCCEEDED!\n");
}
sucesses++;
return 1;
} else {
if(should_print){
printf("TEST FAILED!\n");
}
failures++;
return 0;
}
}

View File

@@ -0,0 +1,179 @@
/* -------------------------------------------------------------------------
* This is an ANSI C library for multi-stream random number generation.
* The use of this library is recommended as a replacement for the ANSI C
* rand() and srand() functions, particularly in simulation applications
* where the statistical 'goodness' of the random number generator is
* important. The library supplies 256 streams of random numbers; use
* SelectStream(s) to switch between streams indexed s = 0,1,...,255.
*
* The streams must be initialized. The recommended way to do this is by
* using the function PlantSeeds(x) with the value of x used to initialize
* the default stream and all other streams initialized automatically with
* values dependent on the value of x. The following convention is used
* to initialize the default stream:
* if x > 0 then x is the state
* if x < 0 then the state is obtained from the system clock
* if x = 0 then the state is to be supplied interactively.
*
* The generator used in this library is a so-called 'Lehmer random number
* generator' which returns a pseudo-random number uniformly distributed
* 0.0 and 1.0. The period is (m - 1) where m = 2,147,483,647 and the
* smallest and largest possible values are (1 / m) and 1 - (1 / m)
* respectively. For more details see:
*
* "Random Number Generators: Good Ones Are Hard To Find"
* Steve Park and Keith Miller
* Communications of the ACM, October 1988
*
* Name : rngs.c (Random Number Generation - Multiple Streams)
* Authors : Steve Park & Dave Geyer
* Language : ANSI C
* Latest Revision : 09-22-98
* -------------------------------------------------------------------------
*/
#include <stdio.h>
#include <time.h>
#include "rngs.h"
#define MODULUS 2147483647 /* DON'T CHANGE THIS VALUE */
#define MULTIPLIER 48271 /* DON'T CHANGE THIS VALUE */
#define CHECK 399268537 /* DON'T CHANGE THIS VALUE */
#define STREAMS 256 /* # of streams, DON'T CHANGE THIS VALUE */
#define A256 22925 /* jump multiplier, DON'T CHANGE THIS VALUE */
#define DEFAULT 123456789 /* initial seed, use 0 < DEFAULT < MODULUS */
static long seed[STREAMS] = {DEFAULT}; /* current state of each stream */
static int stream = 0; /* stream index, 0 is the default */
static int initialized = 0; /* test for stream initialization */
double Random(void)
/* ----------------------------------------------------------------
* Random returns a pseudo-random real number uniformly distributed
* between 0.0 and 1.0.
* ----------------------------------------------------------------
*/
{
const long Q = MODULUS / MULTIPLIER;
const long R = MODULUS % MULTIPLIER;
long t;
t = MULTIPLIER * (seed[stream] % Q) - R * (seed[stream] / Q);
if (t > 0)
seed[stream] = t;
else
seed[stream] = t + MODULUS;
return ((double) seed[stream] / MODULUS);
}
void PlantSeeds(long x)
/* ---------------------------------------------------------------------
* Use this function to set the state of all the random number generator
* streams by "planting" a sequence of states (seeds), one per stream,
* with all states dictated by the state of the default stream.
* The sequence of planted states is separated one from the next by
* 8,367,782 calls to Random().
* ---------------------------------------------------------------------
*/
{
const long Q = MODULUS / A256;
const long R = MODULUS % A256;
int j;
int s;
initialized = 1;
s = stream; /* remember the current stream */
SelectStream(0); /* change to stream 0 */
PutSeed(x); /* set seed[0] */
stream = s; /* reset the current stream */
for (j = 1; j < STREAMS; j++) {
x = A256 * (seed[j - 1] % Q) - R * (seed[j - 1] / Q);
if (x > 0)
seed[j] = x;
else
seed[j] = x + MODULUS;
}
}
void PutSeed(long x)
/* ---------------------------------------------------------------
* Use this function to set the state of the current random number
* generator stream according to the following conventions:
* if x > 0 then x is the state (unless too large)
* if x < 0 then the state is obtained from the system clock
* if x = 0 then the state is to be supplied interactively
* ---------------------------------------------------------------
*/
{
char ok = 0;
if (x > 0)
x = x % MODULUS; /* correct if x is too large */
if (x < 0)
x = ((unsigned long) time((time_t *) NULL)) % MODULUS;
if (x == 0)
while (!ok) {
printf("\nEnter a positive integer seed (9 digits or less) >> ");
scanf("%ld", &x);
ok = (0 < x) && (x < MODULUS);
if (!ok)
printf("\nInput out of range ... try again\n");
}
seed[stream] = x;
}
void GetSeed(long *x)
/* ---------------------------------------------------------------
* Use this function to get the state of the current random number
* generator stream.
* ---------------------------------------------------------------
*/
{
*x = seed[stream];
}
void SelectStream(int index)
/* ------------------------------------------------------------------
* Use this function to set the current random number generator
* stream -- that stream from which the next random number will come.
* ------------------------------------------------------------------
*/
{
stream = ((unsigned int) index) % STREAMS;
if ((initialized == 0) && (stream != 0)) /* protect against */
PlantSeeds(DEFAULT); /* un-initialized streams */
}
void TestRandom(void)
/* ------------------------------------------------------------------
* Use this (optional) function to test for a correct implementation.
* ------------------------------------------------------------------
*/
{
long i;
long x;
/*double u;*/ /* used to be uncommented */
char ok = 0;
SelectStream(0); /* select the default stream */
PutSeed(1); /* and set the state to 1 */
for(i = 0; i < 10000; i++)
Random(); /* used to have u = Random() */
GetSeed(&x); /* get the new state value */
ok = (x == CHECK); /* and check for correctness */
SelectStream(1); /* select stream 1 */
PlantSeeds(1); /* set the state of all streams */
GetSeed(&x); /* get the state of stream 1 */
ok = ok && (x == A256); /* x should be the jump multiplier */
if (ok)
printf("\n The implementation of rngs.c is correct.\n\n");
else
printf("\n\a ERROR -- the implementation of rngs.c is not correct.\n\n");
}

View File

@@ -0,0 +1,19 @@
/* -----------------------------------------------------------------------
* Name : rngs.h (header file for the library file rngs.c)
* Author : Steve Park & Dave Geyer
* Language : ANSI C
* Latest Revision : 09-22-98
* -----------------------------------------------------------------------
*/
#if !defined( _RNGS_ )
#define _RNGS_
double Random(void);
void PlantSeeds(long x);
void GetSeed(long *x);
void PutSeed(long x);
void SelectStream(int index);
void TestRandom(void);
#endif

View File

@@ -0,0 +1,27 @@
#include "rngs.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
if (argc < 3) {
printf ("Not enough inputs: seed target\n");
}
SelectStream(1);
PutSeed((long)atoi(argv[1]));
int done = 0;
int c = 1000000000;
while (!done) {
c = floor(Random() * 1000000000);
// if (c % 100000 == 0) {
// printf ("c = %d\n", c);
// }
if (c == atoi(argv[2])) {
printf ("Found the bug!\n");
done = 1;
}
}
}

View File

@@ -0,0 +1,30 @@
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"
#define DEBUG 0
#define NOISY_TEST 1
int main () {
int r;
int k[10] = {adventurer, council_room, feast, gardens, mine,
remodel, smithy, village, baron, great_hall};
struct gameState G;
r = initializeGame(4, k, 1, &G);
printf ("initializeGame(4, k, 1, &G) = %d\n", r);
assert(r == 0);
r = supplyCount(adventurer, &G);
printf ("supplyCount(adventurer, &G) = %d\n", r);
assert(r == 10);
return 0;
}

View File

@@ -0,0 +1,94 @@
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"
#define DEBUG 0
#define NOISY_TEST 1
int checkDrawCard(int p, struct gameState *post) {
struct gameState pre;
memcpy (&pre, post, sizeof(struct gameState));
int r;
// printf ("drawCard PRE: p %d HC %d DeC %d DiC %d\n",
// p, pre.handCount[p], pre.deckCount[p], pre.discardCount[p]);
r = drawCard (p, post);
//printf ("drawCard POST: p %d HC %d DeC %d DiC %d\n",
// p, post->handCount[p], post->deckCount[p], post->discardCount[p]);
if (pre.deckCount[p] > 0) {
pre.handCount[p]++;
pre.hand[p][pre.handCount[p]-1] = pre.deck[p][pre.deckCount[p]-1];
pre.deckCount[p]--;
} else if (pre.discardCount[p] > 0) {
memcpy(pre.deck[p], post->deck[p], sizeof(int) * pre.discardCount[p]);
memcpy(pre.discard[p], post->discard[p], sizeof(int)*pre.discardCount[p]);
pre.hand[p][post->handCount[p]-1] = post->hand[p][post->handCount[p]-1];
pre.handCount[p]++;
pre.deckCount[p] = pre.discardCount[p]-1;
pre.discardCount[p] = 0;
}
assert (r == 0);
assert(memcmp(&pre, post, sizeof(struct gameState)) == 0);
}
int main () {
int i, n, r, p, deckCount, discardCount, handCount;
int k[10] = {adventurer, council_room, feast, gardens, mine,
remodel, smithy, village, baron, great_hall};
struct gameState G;
printf ("Testing buyCard.\n");
printf ("RANDOM TESTS.\n");
SelectStream(2);
PutSeed(3);
for (n = 0; n < 2000; n++) {
for (i = 0; i < sizeof(struct gameState); i++) {
((char*)&G)[i] = floor(Random() * 256);
}
p = floor(Random() * 2);
G.deckCount[p] = floor(Random() * MAX_DECK);
G.discardCount[p] = floor(Random() * MAX_DECK);
G.handCount[p] = floor(Random() * MAX_HAND);
checkDrawCard(p, &G);
}
printf ("ALL TESTS OK\n");
exit(0);
printf ("SIMPLE FIXED TESTS.\n");
for (p = 0; p < 2; p++) {
for (deckCount = 0; deckCount < 5; deckCount++) {
for (discardCount = 0; discardCount < 5; discardCount++) {
for (handCount = 0; handCount < 5; handCount++) {
memset(&G, 23, sizeof(struct gameState));
r = initializeGame(2, k, 1, &G);
G.deckCount[p] = deckCount;
memset(G.deck[p], 0, sizeof(int) * deckCount);
G.discardCount[p] = discardCount;
memset(G.discard[p], 0, sizeof(int) * discardCount);
G.handCount[p] = handCount;
memset(G.hand[p], 0, sizeof(int) * handCount);
checkDrawCard(p, &G);
}
}
}
}
return 0;
}

View File

@@ -0,0 +1,94 @@
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include "rngs.h"
#define DEBUG 0
#define NOISY_TEST 1
int checkDrawCard(int p, struct gameState *post) {
struct gameState pre;
memcpy (&pre, post, sizeof(struct gameState));
int r;
// printf ("drawCard PRE: p %d HC %d DeC %d DiC %d\n",
// p, pre.handCount[p], pre.deckCount[p], pre.discardCount[p]);
r = drawCard (p, post);
//printf ("drawCard POST: p %d HC %d DeC %d DiC %d\n",
// p, post->handCount[p], post->deckCount[p], post->discardCount[p]);
if (pre.deckCount[p] > 0) {
pre.handCount[p]++;
pre.hand[p][pre.handCount[p]-1] = pre.deck[p][pre.deckCount[p]-1];
pre.deckCount[p]--;
} else if (pre.discardCount[p] > 0) {
memcpy(pre.deck[p], post->deck[p], sizeof(int) * pre.discardCount[p]);
memcpy(pre.discard[p], post->discard[p], sizeof(int)*pre.discardCount[p]);
pre.hand[p][post->handCount[p]-1] = post->hand[p][post->handCount[p]-1];
pre.handCount[p]++;
pre.deckCount[p] = pre.discardCount[p]-1;
pre.discardCount[p] = 0;
}
assert (r == 0);
assert(memcmp(&pre, post, sizeof(struct gameState)) == 0);
}
int main () {
int i, n, r, p, deckCount, discardCount, handCount;
int k[10] = {adventurer, council_room, feast, gardens, mine,
remodel, smithy, village, baron, great_hall};
struct gameState G;
printf ("Testing drawCard.\n");
printf ("RANDOM TESTS.\n");
SelectStream(2);
PutSeed(3);
for (n = 0; n < 2000; n++) {
for (i = 0; i < sizeof(struct gameState); i++) {
((char*)&G)[i] = floor(Random() * 256);
}
p = floor(Random() * 2);
G.deckCount[p] = floor(Random() * MAX_DECK);
G.discardCount[p] = floor(Random() * MAX_DECK);
G.handCount[p] = floor(Random() * MAX_HAND);
checkDrawCard(p, &G);
}
printf ("ALL TESTS OK\n");
exit(0);
printf ("SIMPLE FIXED TESTS.\n");
for (p = 0; p < 2; p++) {
for (deckCount = 0; deckCount < 5; deckCount++) {
for (discardCount = 0; discardCount < 5; discardCount++) {
for (handCount = 0; handCount < 5; handCount++) {
memset(&G, 23, sizeof(struct gameState));
r = initializeGame(2, k, 1, &G);
G.deckCount[p] = deckCount;
memset(G.deck[p], 0, sizeof(int) * deckCount);
G.discardCount[p] = discardCount;
memset(G.discard[p], 0, sizeof(int) * discardCount);
G.handCount[p] = handCount;
memset(G.hand[p], 0, sizeof(int) * handCount);
checkDrawCard(p, &G);
}
}
}
}
return 0;
}

View File

@@ -0,0 +1,56 @@
#include "dominion.h"
#include <stdio.h>
#include <stdlib.h>
#include "rngs.h"
int main (int argc, char** argv) {
struct gameState G;
int i;
int start = -1;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
memset(&G, 'z', sizeof(struct gameState));
initializeGame(4, k, atoi(argv[1]), &G);
printf ("Rough guide to locations in structure:\n");
printf ("0: numPlayers\n");
printf ("%ld: supplyCount[0]\n", ((long)&(G.supplyCount[0]))-((long)&G));
printf ("%ld: embargoTokens[0]\n", ((long)&(G.embargoTokens[0]))-((long)&G));
printf ("%ld: hand[0][0]\n", ((long)&(G.hand[0][0]))-(long)(&G));
printf ("%ld: deck[0][0]\n", ((long)&(G.deck[0][0]))-(long)(&G));
printf ("%ld: discard[0][0]\n", ((long)&(G.discard[0][0]))-(long)(&G));
printf ("%ld: playerCards[0]\n", ((long)&(G.playedCards[0]))-(long)(&G));
for (i = 0; i < sizeof(struct gameState); i++) {
if (((char*)&G)[i] == 'z') {
if (start == -1) {
start = i;
}
} else{
if (start != -1) {
if (start == (i-1)) {
printf ("Byte %d uninitialized.\n", start);
} else {
printf ("Bytes %d-%d uninitialized.\n", start, i-1);
}
start = -1;
}
}
}
if (start != -1) {
if (start == (i-1)) {
printf ("Byte %d uninitialized.\n", start);
} else {
printf ("Bytes %d-%d uninitialized.\n", start, i-1);
}
}
return 0;
}

View File

@@ -0,0 +1,27 @@
#include "dominion.h"
#include <stdio.h>
#include <assert.h>
int compare(const int* a, const int* b);
int main () {
struct gameState G;
struct gameState G2;
// Initialize G.
memcpy (&G2, &G, sizeof(struct gameState));
int ret = shuffle(0,&G);
if (G.deckCount[0] > 0) {
assert (ret != -1);
qsort ((void*)(G.deck[0]), G.deckCount[0], sizeof(int), compare);
qsort ((void*)(G2.deck[0]), G2.deckCount[0], sizeof(int), compare);
} else
assert (ret == -1);
assert(memcmp(&G, &G2, sizeof(struct gameState)) == 0);
}

View File

@@ -0,0 +1,69 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
initializeGame(2, k, 65432, &G);
//If province cards are empty or are least three supply piles are, game end
//else game is not over. 1 is game end, 0 is game continues
unsigned int victory_cards_max = 12;
unsigned int num_supply_piles = treasure_map+1;
unsigned int max_count_for_any_supply = 60;
int i;
for (i = 0; i < victory_cards_max; i++) {
int num_zeroed = 0;
for (int k = 0; k < num_supply_piles; k++) {
G.supplyCount[k] = max_count_for_any_supply;
}
for (; num_zeroed < num_supply_piles; num_zeroed++) {
for (int j = 0; j < num_zeroed; j++) {
G.supplyCount[j] = 0;
}
G.supplyCount[province] = i;
if ((G.supplyCount[province] == 0) || (num_zeroed >= 3)) {
testing_assert(isGameOver(&G) == 1);
} else {
testing_assert(isGameOver(&G) == 0);
}
}
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,88 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int min_players = 2;
unsigned int max_players = MAX_PLAYERS;
unsigned int max_hand = MAX_HAND;
unsigned int arbitrary_bonus_max = 1000;
unsigned int copper_worth = 1;
unsigned int silver_worth = 2;
unsigned int gold_worth = 3;
for(int i = min_players ; i <= max_players ; i++){
initializeGame(i, k, 65432, &G);
for(int j = 0 ; j < i ; j++){
G.handCount[j] = max_hand;
for(int k = 0 ; k < max_hand ; k++){
G.hand[j][k] = copper;
}
updateCoins(j, &G, 0);
testing_assert(G.coins == (max_hand*copper_worth));
updateCoins(j, &G, arbitrary_bonus_max);
testing_assert(G.coins == ((max_hand*copper_worth) + arbitrary_bonus_max));
for(int k = 0 ; k < max_hand ; k++){
G.hand[j][k] = silver;
}
updateCoins(j, &G, 0);
testing_assert(G.coins == (max_hand*silver_worth));
updateCoins(j, &G, arbitrary_bonus_max);
testing_assert(G.coins == ((max_hand*silver_worth) + arbitrary_bonus_max));
for(int k = 0 ; k < max_hand ; k++){
G.hand[j][k] = gold;
}
updateCoins(j, &G, 0);
testing_assert(G.coins == (max_hand*gold_worth));
updateCoins(j, &G, arbitrary_bonus_max);
testing_assert(G.coins == ((max_hand*gold_worth) + arbitrary_bonus_max));
}
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,47 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
for(int i = 2 ; i <= MAX_PLAYERS ; i++){
initializeGame(i, k, 65432, &G);
for(int j = 0 ; j < i ; j++){
G.whoseTurn = j;
testing_assert(whoseTurn(&G) == j);
}
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,55 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression);
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
initializeGame(4, k, 65432, &G);
unsigned int num_card_types = 27;
for(int i = 0 ; i < MAX_PLAYERS ; i++){
G.whoseTurn = i;
G.handCount[i] = MAX_HAND;
for(int j = 0 ; j < MAX_HAND ; j++){
for(int k = 0 ; k < num_card_types ; k++){
G.hand[i][j] = k;
testing_assert(handCard(j, &G) == k);
}
}
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression) {
if (expression) {
printf("TEST SUCCEEDED!\n");
return 1;
} else {
printf("TEST FAILED!\n");
return 0;
}
}

View File

@@ -0,0 +1,60 @@
#############################
########## Bug #01 ##########
#############################
Title: playSmithy Incorrect Card Pickup Count
Affected Files: dominion.c
Pertinent Code:
dominion.c: Line 717
Test Environment: Red Hat Linux 4.8.5-4 (Kernel 3.10.0)
Expected Result:
A call to playSmithy should add three cards to the player's hand, then
discard the played card. The final result should be a hand with two extra cards
in it.
Actual Result:
playSmithy picks up five cards instead of three, then discards a card like
normal. The result is a hand with four extra cards instead of two.
Steps to Reproduce:
1. Use the other game functions to set up a normal game state.
2. Set a current player.
3. Save the handcount for the player in a temp variable.
4. Call playSmithy against the selected player.
5. Compare the saved handcount to the new handcount to see incorrect number.
#############################
########## Bug #02 ##########
#############################
Title: playAdventurer Incorrect Hand Count When Drawing Non-Treasure Cards
Affected Files: dominion.c
Pertinent Code:
dominion.c: Line 701
Test Environment: Red Hat Linux 4.8.5-4 (Kernel 3.10.0)
Expected Result:
In a call to playAdventurer, during the process of "picking up" cards,
the temp hand stores a copy of any cards that aren't treasure cards, then
removes the just saved "top card". This allows cards to be shuffled back into
the game without being lost.
Actual Result:
In a call to playAdventurer, during the process of "picking up" cards,
instead of removing the top card, an invalid top card is instead added.
Depending on the previous state of the hand, the error may not show itself,
for example, if every card picked up were a treasure card. In the case where
this bug is hit, invalid cards could be shuffled back into the players hand at
a later time, causing major bugs in gameplay.
Steps to Reproduce:
1. Use the other game functions to set up a normal game state.
2. Set a current player.
4. Call playAdventurer against the selected player.
5. Manually inspect the hand of the player and note invalid card indexes.

View File

@@ -0,0 +1,27 @@
CFLAGS = -Wall -fpic -coverage -lm -std=c99 -ftest-coverage -fprofile-arcs
all: assignment5
assignment5:
echo "Compiling and running all tests....." > unittestresults.out
gcc -o cardtest2 cardtest2.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Cardtest2...." >> unittestresults.out
./cardtest2 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> cardtest2.gcov
echo "######################################" >> unittestresults.out
echo "######################################" >> unittestresults.out
gcc -o cardtest3 cardtest3.c dominion.c rngs.c -g $(CFLAGS)
echo "Running Cardtest3...." >> unittestresults.out
./cardtest3 >> unittestresults.out
gcov dominion.c >> unittestresults.out
cat dominion.c.gcov >> unittestresults.out
cat dominion.c.gcov >> cardtest3.gcov
clean:
rm -f *.o playdom.exe playdom test.exe test player player.exe testInit testInit.exe *.gcov *.gcda *.gcno *.so *.out
rm -f cardtest2 cardtest3

View File

@@ -0,0 +1,85 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression, int should_print);
long sucesses = 0;
long failures = 0;
int main(int argc, char** argv) {
struct gameState G, G_copy;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int arbitrary_hand_count_max = MAX_HAND - 3;
for(int l = 0 ; l < 4 ; l++){
for(int i = 1 ; i < arbitrary_hand_count_max ; i++){
initializeGame(4, k, 65432, &G);
G.handCount[0] = arbitrary_hand_count_max;
G.whoseTurn = l;
G_copy = G;
drawCard(0, &G_copy);
drawCard(0, &G_copy);
drawCard(0, &G_copy);
discardCard(i, l, &G_copy, 0);
playSmithy(&G, i, G.whoseTurn);
testing_assert(G.handCount[l] == G_copy.handCount[l], 0);
for(int j = 0 ; j < arbitrary_hand_count_max ; j++){
testing_assert((G.hand[l][j] >= curse) && (G.hand[l][j] <= treasure_map), 0);
testing_assert((G_copy.hand[l][j] >= curse) && (G_copy.hand[l][j] <= treasure_map), 0);
}
}
}
printf("Run complete!\n");
printf("SUCCESSES: %ld\n", sucesses);
if(failures > 0){
printf("Some tests failed!!!\n");
printf("FAILURES: %ld\n", failures);
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression, int should_print) {
if (expression) {
if(should_print){
printf("TEST SUCCEEDED!\n");
}
sucesses++;
return 1;
} else {
if(should_print){
printf("TEST FAILED!\n");
}
failures++;
return 0;
}
}

View File

@@ -0,0 +1,92 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: unittest1.c
* Author: corwinperren
*
* Created on February 1, 2017, 9:02 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
int testing_assert(int expression, int should_print);
long sucesses = 0;
long failures = 0;
int main(int argc, char** argv) {
struct gameState G;
int k[10] = {adventurer, gardens, embargo, village, minion, mine, cutpurse,
sea_hag, tribute, smithy};
unsigned int arbitrary_hand_count_max = MAX_HAND - 2;
for(int l = 0 ; l < 4 ; l++){
for(int i = 1 ; i < arbitrary_hand_count_max ; i++){
initializeGame(4, k, 65432, &G);
G.handCount[l] = arbitrary_hand_count_max;
G.whoseTurn = l;
int temp_hand[MAX_HAND];
playAdventurer(0, &G, G.whoseTurn, temp_hand, 0);
testing_assert(G.handCount[l] == arbitrary_hand_count_max + 2, 0);
testing_assert((G.hand[l][G.handCount[l] - 1] >= copper) && (G.hand[l][G.handCount[l] - 1] <= gold), 0);
testing_assert((G.hand[l][G.handCount[l] - 2] >= copper) && (G.hand[l][G.handCount[l] - 2] <= gold), 0);
initializeGame(4, k, 65432, &G);
G.handCount[l] = arbitrary_hand_count_max;
G.whoseTurn = l;
G.deckCount[l] = 0;
for(int m = 0 ; m < MAX_DECK ; m += 3){
G.discard[l][m] = copper;
G.discard[l][m + 1] = silver;
G.discard[l][m + 2] = gold;
G.discardCount[l] += 3;
}
playAdventurer(0, &G, G.whoseTurn, temp_hand, 0);
testing_assert(G.handCount[l] == arbitrary_hand_count_max + 2, 0);
testing_assert((G.hand[l][G.handCount[l] - 1] >= copper) && (G.hand[l][G.handCount[l] - 1] <= gold), 0);
testing_assert((G.hand[l][G.handCount[l] - 2] >= copper) && (G.hand[l][G.handCount[l] - 2] <= gold), 0);
}
}
printf("Run complete!\n");
printf("SUCCESSES: %ld\n", sucesses);
if(failures > 0){
printf("Some tests failed!!!\n");
printf("FAILURES: %ld\n", failures);
}
return (EXIT_SUCCESS);
}
int testing_assert(int expression, int should_print) {
if (expression) {
if(should_print){
printf("TEST SUCCEEDED!\n");
}
sucesses++;
return 1;
} else {
if(should_print){
printf("TEST FAILED!\n");
}
failures++;
return 0;
}
}

View File

@@ -0,0 +1,143 @@
#ifndef _DOMINION_H
#define _DOMINION_H
// Code from various sources, baseline from Kristen Bartosz
#define MAX_HAND 500
#define MAX_DECK 500
#define MAX_PLAYERS 4
#define DEBUG 0
/* http://dominion.diehrstraits.com has card texts */
/* http://dominion.isotropic.org has other stuff */
/* hand# means index of a card in current active player's hand */
enum CARD
{curse = 0,
estate, //1
duchy, //2
province, //3
copper, //4
silver, //5
gold, //6
adventurer, //7
/* If no/only 1 treasure found, stop when full deck seen */
council_room, //8
feast, //9 /* choice1 is supply # of card gained) */
gardens, //10
mine, //11 /* choice1 is hand# of money to trash, choice2 is supply# of
//money to put in hand */
remodel, //12 /* choice1 is hand# of card to remodel, choice2 is supply# */
smithy, //13
village, //14
baron, //15 /* choice1: boolean for discard of estate */
/* Discard is always of first (lowest index) estate */
great_hall, //16
minion, //17 /* choice1: 1 = +2 coin, 2 = redraw */
steward, //18 /* choice1: 1 = +2 card, 2 = +2 coin, 3 = trash 2 (choice2,3) */
tribute, //19
ambassador, //20 /* choice1 = hand#, choice2 = number to return to supply */
cutpurse, //21
embargo, //22 /* choice1 = supply# */
outpost, //23
salvager, //24 /* choice1 = hand# to trash */
sea_hag, //25
treasure_map //26
};
struct gameState {
int numPlayers; //number of players
int supplyCount[treasure_map+1]; //this is the amount of a specific type of card given a specific number.
int embargoTokens[treasure_map+1];
int outpostPlayed;
int outpostTurn;
int whoseTurn;
int phase;
int numActions; /* Starts at 1 each turn */
int coins; /* Use as you see fit! */
int numBuys; /* Starts at 1 each turn */
int hand[MAX_PLAYERS][MAX_HAND];
int handCount[MAX_PLAYERS];
int deck[MAX_PLAYERS][MAX_DECK];
int deckCount[MAX_PLAYERS];
int discard[MAX_PLAYERS][MAX_DECK];
int discardCount[MAX_PLAYERS];
int playedCards[MAX_DECK];
int playedCardCount;
};
/* All functions return -1 on failure, and DO NOT CHANGE GAME STATE;
unless specified for other return, return 0 on success */
struct gameState* newGame();
int* kingdomCards(int k1, int k2, int k3, int k4, int k5, int k6, int k7,
int k8, int k9, int k10);
int initializeGame(int numPlayers, int kingdomCards[10], int randomSeed,
struct gameState *state);
/* Responsible for initializing all supplies, and shuffling deck and
drawing starting hands for all players. Check that 10 cards selected
are in fact (different) kingdom cards, and that numPlayers is valid.
Cards not in game should initialize supply position to -1 */
int shuffle(int player, struct gameState *state);
/* Assumes all cards are now in deck array (or hand/played): discard is
empty */
int playCard(int handPos, int choice1, int choice2, int choice3,
struct gameState *state);
/* Play card with index handPos from current player's hand */
int buyCard(int supplyPos, struct gameState *state);
/* Buy card with supply index supplyPos */
int numHandCards(struct gameState *state);
/* How many cards current player has in hand */
int handCard(int handNum, struct gameState *state);
/* enum value of indexed card in player's hand */
int supplyCount(int card, struct gameState *state);
/* How many of given card are left in supply */
int fullDeckCount(int player, int card, struct gameState *state);
/* Here deck = hand + discard + deck */
int whoseTurn(struct gameState *state);
int endTurn(struct gameState *state);
/* Must do phase C and advance to next player; do not advance whose turn
if game is over */
int isGameOver(struct gameState *state);
int scoreFor(int player, struct gameState *state);
/* Negative here does not mean invalid; scores may be negative,
-9999 means invalid input */
int getWinners(int players[MAX_PLAYERS], struct gameState *state);
/* Set array position of each player who won (remember ties!) to
1, others to 0 */
/*int playAdventurer(struct gameState *state);
int playSmithy(struct gameState *state, int handPos);
int playVillage(struct gameState *state, int handPos);
int playFeast(struct gameState *state, int choice1);
int playCouncil_Room(struct gameState *state, int handPos);*/
int playAdventurer(int drawntreasure, struct gameState *state, int currentPlayer, int temphand[MAX_HAND], int z);
int playSmithy(struct gameState *state, int handPos, int currentPlayer);
int playVillage(int currentPlayer, struct gameState *state, int handPos);
#endif

View File

@@ -0,0 +1,15 @@
#ifndef _DOMINION_HELPERS_H
#define _DOMINION_HELPERS_H
#include "dominion.h"
int drawCard(int player, struct gameState *state);
int updateCoins(int player, struct gameState *state, int bonus);
int discardCard(int handPos, int currentPlayer, struct gameState *state,
int trashFlag);
int gainCard(int supplyPos, struct gameState *state, int toFlag, int player);
int getCost(int cardNumber);
int cardEffect(int card, int choice1, int choice2, int choice3,
struct gameState *state, int handPos, int *bonus);
#endif

View File

@@ -0,0 +1,179 @@
/* -------------------------------------------------------------------------
* This is an ANSI C library for multi-stream random number generation.
* The use of this library is recommended as a replacement for the ANSI C
* rand() and srand() functions, particularly in simulation applications
* where the statistical 'goodness' of the random number generator is
* important. The library supplies 256 streams of random numbers; use
* SelectStream(s) to switch between streams indexed s = 0,1,...,255.
*
* The streams must be initialized. The recommended way to do this is by
* using the function PlantSeeds(x) with the value of x used to initialize
* the default stream and all other streams initialized automatically with
* values dependent on the value of x. The following convention is used
* to initialize the default stream:
* if x > 0 then x is the state
* if x < 0 then the state is obtained from the system clock
* if x = 0 then the state is to be supplied interactively.
*
* The generator used in this library is a so-called 'Lehmer random number
* generator' which returns a pseudo-random number uniformly distributed
* 0.0 and 1.0. The period is (m - 1) where m = 2,147,483,647 and the
* smallest and largest possible values are (1 / m) and 1 - (1 / m)
* respectively. For more details see:
*
* "Random Number Generators: Good Ones Are Hard To Find"
* Steve Park and Keith Miller
* Communications of the ACM, October 1988
*
* Name : rngs.c (Random Number Generation - Multiple Streams)
* Authors : Steve Park & Dave Geyer
* Language : ANSI C
* Latest Revision : 09-22-98
* -------------------------------------------------------------------------
*/
#include <stdio.h>
#include <time.h>
#include "rngs.h"
#define MODULUS 2147483647 /* DON'T CHANGE THIS VALUE */
#define MULTIPLIER 48271 /* DON'T CHANGE THIS VALUE */
#define CHECK 399268537 /* DON'T CHANGE THIS VALUE */
#define STREAMS 256 /* # of streams, DON'T CHANGE THIS VALUE */
#define A256 22925 /* jump multiplier, DON'T CHANGE THIS VALUE */
#define DEFAULT 123456789 /* initial seed, use 0 < DEFAULT < MODULUS */
static long seed[STREAMS] = {DEFAULT}; /* current state of each stream */
static int stream = 0; /* stream index, 0 is the default */
static int initialized = 0; /* test for stream initialization */
double Random(void)
/* ----------------------------------------------------------------
* Random returns a pseudo-random real number uniformly distributed
* between 0.0 and 1.0.
* ----------------------------------------------------------------
*/
{
const long Q = MODULUS / MULTIPLIER;
const long R = MODULUS % MULTIPLIER;
long t;
t = MULTIPLIER * (seed[stream] % Q) - R * (seed[stream] / Q);
if (t > 0)
seed[stream] = t;
else
seed[stream] = t + MODULUS;
return ((double) seed[stream] / MODULUS);
}
void PlantSeeds(long x)
/* ---------------------------------------------------------------------
* Use this function to set the state of all the random number generator
* streams by "planting" a sequence of states (seeds), one per stream,
* with all states dictated by the state of the default stream.
* The sequence of planted states is separated one from the next by
* 8,367,782 calls to Random().
* ---------------------------------------------------------------------
*/
{
const long Q = MODULUS / A256;
const long R = MODULUS % A256;
int j;
int s;
initialized = 1;
s = stream; /* remember the current stream */
SelectStream(0); /* change to stream 0 */
PutSeed(x); /* set seed[0] */
stream = s; /* reset the current stream */
for (j = 1; j < STREAMS; j++) {
x = A256 * (seed[j - 1] % Q) - R * (seed[j - 1] / Q);
if (x > 0)
seed[j] = x;
else
seed[j] = x + MODULUS;
}
}
void PutSeed(long x)
/* ---------------------------------------------------------------
* Use this function to set the state of the current random number
* generator stream according to the following conventions:
* if x > 0 then x is the state (unless too large)
* if x < 0 then the state is obtained from the system clock
* if x = 0 then the state is to be supplied interactively
* ---------------------------------------------------------------
*/
{
char ok = 0;
if (x > 0)
x = x % MODULUS; /* correct if x is too large */
if (x < 0)
x = ((unsigned long) time((time_t *) NULL)) % MODULUS;
if (x == 0)
while (!ok) {
printf("\nEnter a positive integer seed (9 digits or less) >> ");
scanf("%ld", &x);
ok = (0 < x) && (x < MODULUS);
if (!ok)
printf("\nInput out of range ... try again\n");
}
seed[stream] = x;
}
void GetSeed(long *x)
/* ---------------------------------------------------------------
* Use this function to get the state of the current random number
* generator stream.
* ---------------------------------------------------------------
*/
{
*x = seed[stream];
}
void SelectStream(int index)
/* ------------------------------------------------------------------
* Use this function to set the current random number generator
* stream -- that stream from which the next random number will come.
* ------------------------------------------------------------------
*/
{
stream = ((unsigned int) index) % STREAMS;
if ((initialized == 0) && (stream != 0)) /* protect against */
PlantSeeds(DEFAULT); /* un-initialized streams */
}
void TestRandom(void)
/* ------------------------------------------------------------------
* Use this (optional) function to test for a correct implementation.
* ------------------------------------------------------------------
*/
{
long i;
long x;
/*double u;*/ /* used to be uncommented */
char ok = 0;
SelectStream(0); /* select the default stream */
PutSeed(1); /* and set the state to 1 */
for(i = 0; i < 10000; i++)
Random(); /* used to have u = Random() */
GetSeed(&x); /* get the new state value */
ok = (x == CHECK); /* and check for correctness */
SelectStream(1); /* select stream 1 */
PlantSeeds(1); /* set the state of all streams */
GetSeed(&x); /* get the state of stream 1 */
ok = ok && (x == A256); /* x should be the jump multiplier */
if (ok)
printf("\n The implementation of rngs.c is correct.\n\n");
else
printf("\n\a ERROR -- the implementation of rngs.c is not correct.\n\n");
}

View File

@@ -0,0 +1,19 @@
/* -----------------------------------------------------------------------
* Name : rngs.h (header file for the library file rngs.c)
* Author : Steve Park & Dave Geyer
* Language : ANSI C
* Latest Revision : 09-22-98
* -----------------------------------------------------------------------
*/
#if !defined( _RNGS_ )
#define _RNGS_
double Random(void);
void PlantSeeds(long x);
void GetSeed(long *x);
void PutSeed(long x);
void SelectStream(int index);
void TestRandom(void);
#endif

View File

@@ -0,0 +1,7 @@
CFLAGS = -Wall -fpic -coverage -lm -std=c99 -ftest-coverage -fprofile-arcs
all:
gcc -o testme -g testme.c $(CFLAGS)
clean:
rm -f testme testmeresults.out

View File

@@ -0,0 +1,20 @@
/*
* My approach to writing the tests such that it found the error randomly, while
* still completing in the shortest possible of time. To do this, I made a char
* array that contained only the chars that were tested for to maximize
* potential for creating valid strings, and for moving through the states.
* To come up with a char, I simply used mod against a call to rand such that it
* gave me the index for a character from the array I made.
*
* For creating valid strings, I allocated memory for the five character string
* that would hopefully eventually become the word "reset" and added and extra
* spot for the null character. I then called my completed inputChar to get a
* random char and place it in each of the five spots in the array. Finally, I
* add in the null terminating character.
*
* In practice, running this does exactly what I want. Due to traversal of
* states being a simple matter of getting the right characters, the program's
* state moves to nine incredibly fast. Then, all that's left is to iterate over
* the random possibilities for strings until it comes up with the word "reset",
* at which point the program prints "error" and exits.
*/

View File

@@ -0,0 +1,84 @@
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define NUM_VALID_CHARS 13
#define STRING_LENGTH 5
char valid_chars[NUM_VALID_CHARS] = {
'(',
'[',
'{',
')',
']',
'}',
' ',
'a',
'x',
'r',
'e',
's',
't'
};
char inputChar()
{
return valid_chars[rand() % NUM_VALID_CHARS];
}
char *inputString()
{
char *temp_string = malloc((STRING_LENGTH + 1) * sizeof(char));
for(int i = 0 ; i < STRING_LENGTH ; i++){
temp_string[i] = inputChar();
}
temp_string[STRING_LENGTH] = '\0';
return temp_string;
}
void testme()
{
int tcCount = 0;
char *s;
char c;
int state = 0;
while (1)
{
tcCount++;
c = inputChar();
s = inputString();
printf("Iteration %d: c = %c, s = %s, state = %d\n", tcCount, c, s, state);
if (c == '[' && state == 0) state = 1;
if (c == '(' && state == 1) state = 2;
if (c == '{' && state == 2) state = 3;
if (c == ' '&& state == 3) state = 4;
if (c == 'a' && state == 4) state = 5;
if (c == 'x' && state == 5) state = 6;
if (c == '}' && state == 6) state = 7;
if (c == ')' && state == 7) state = 8;
if (c == ']' && state == 8) state = 9;
if (s[0] == 'r' && s[1] == 'e'
&& s[2] == 's' && s[3] == 'e'
&& s[4] == 't' && s[5] == '\0'
&& state == 9)
{
printf("error ");
free(s);
exit(200);
}
free(s);
}
}
int main(int argc, char *argv[])
{
srand(time(NULL));
testme();
return 0;
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<component inherit-compiler-output="true" inheritJdk="true">
<output-test url="file://$MODULE_DIR$/out/test/URLValidator"/>
<exclude-output/>
<contentEntry url="file://$MODULE_DIR$"/>
</component>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="eclipse" classpath-dir="$MODULE_DIR$" type="JAVA_MODULE" version="4" />

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<eclipse-userlibraries />

View File

@@ -0,0 +1,370 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/**
* <p><b>Domain name</b> validation routines.</p>
*
* <p>
* This validator provides methods for validating Internet domain names
* and top-level domains.
* </p>
*
* <p>Domain names are evaluated according
* to the standards <a href="http://www.ietf.org/rfc/rfc1034.txt">RFC1034</a>,
* section 3, and <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC1123</a>,
* section 2.1. No accomodation is provided for the specialized needs of
* other applications; if the domain name has been URL-encoded, for example,
* validation will fail even though the equivalent plaintext version of the
* same name would have passed.
* </p>
*
* <p>
* Validation is also provided for top-level domains (TLDs) as defined and
* maintained by the Internet Assigned Numbers Authority (IANA):
* </p>
*
* <ul>
* <li>{@link #isValidInfrastructureTld} - validates infrastructure TLDs
* (<code>.arpa</code>, etc.)</li>
* <li>{@link #isValidGenericTld} - validates generic TLDs
* (<code>.com, .org</code>, etc.)</li>
* <li>{@link #isValidCountryCodeTld} - validates country code TLDs
* (<code>.us, .uk, .cn</code>, etc.)</li>
* </ul>
*
* <p>
* (<b>NOTE</b>: This class does not provide IP address lookup for domain names or
* methods to ensure that a given domain name matches a specific IP; see
* {@link java.net.InetAddress} for that functionality.)
* </p>
*
* @version $Revision: 1227719 $ $Date: 2012-01-05 09:45:51 -0800 (Thu, 05 Jan 2012) $
* @since Validator 1.4
*/
public class DomainValidator implements Serializable {
private static final long serialVersionUID = -4407125112880174009L;
// Regular expression strings for hostnames (derived from RFC2396 and RFC 1123)
private static final String DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]*\\p{Alnum})*";
private static final String TOP_LABEL_REGEX = "\\p{Alpha}{2,}";
//christia : bug introduced by arpit
//private static final String TOP_LABEL_REGEX = "\\p{A-Z}{2,}";
private static final String DOMAIN_NAME_REGEX =
"^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")$";
private final boolean allowLocal;
/**
* Singleton instance of this validator, which
* doesn't consider local addresses as valid.
*/
private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false);
/**
* Singleton instance of this validator, which does
* consider local addresses valid.
*/
private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true);
/**
* RegexValidator for matching domains.
*/
private final RegexValidator domainRegex =
new RegexValidator(DOMAIN_NAME_REGEX);
/**
* RegexValidator for matching the a local hostname
*/
private final RegexValidator hostnameRegex =
new RegexValidator(DOMAIN_LABEL_REGEX);
/**
* Returns the singleton instance of this validator. It
* will not consider local addresses as valid.
* @return the singleton instance of this validator
*/
public static DomainValidator getInstance() {
return DOMAIN_VALIDATOR;
}
/**
* Returns the singleton instance of this validator,
* with local validation as required.
* @param allowLocal Should local addresses be considered valid?
* @return the singleton instance of this validator
*/
public static DomainValidator getInstance(boolean allowLocal) {
if(allowLocal) {
return DOMAIN_VALIDATOR_WITH_LOCAL;
}
return DOMAIN_VALIDATOR;
}
/** Private constructor. */
private DomainValidator(boolean allowLocal) {
this.allowLocal = allowLocal;
}
/**
* Returns true if the specified <code>String</code> parses
* as a valid domain name with a recognized top-level domain.
* The parsing is case-sensitive.
* @param domain the parameter to check for domain name syntax
* @return true if the parameter is a valid domain name
*/
public boolean isValid(String domain) {
String[] groups = domainRegex.match(domain);
if (groups != null && groups.length > 0) {
return isValidTld(groups[0]);
} else if(allowLocal) {
if (!hostnameRegex.isValid(domain)) {
return true;
}
}
return false;
}
/**
* Returns true if the specified <code>String</code> matches any
* IANA-defined top-level domain. Leading dots are ignored if present.
* The search is case-sensitive.
* @param tld the parameter to check for TLD status
* @return true if the parameter is a TLD
*/
public boolean isValidTld(String tld) {
if(allowLocal && isValidLocalTld(tld)) {
return true;
}
return isValidInfrastructureTld(tld)
|| isValidGenericTld(tld)
|| isValidCountryCodeTld(tld);
}
/**
* Returns true if the specified <code>String</code> matches any
* IANA-defined infrastructure top-level domain. Leading dots are
* ignored if present. The search is case-sensitive.
* @param iTld the parameter to check for infrastructure TLD status
* @return true if the parameter is an infrastructure TLD
*/
public boolean isValidInfrastructureTld(String iTld) {
return INFRASTRUCTURE_TLD_LIST.contains(chompLeadingDot(iTld.toLowerCase()));
}
/**
* Returns true if the specified <code>String</code> matches any
* IANA-defined generic top-level domain. Leading dots are ignored
* if present. The search is case-sensitive.
* @param gTld the parameter to check for generic TLD status
* @return true if the parameter is a generic TLD
*/
public boolean isValidGenericTld(String gTld) {
return GENERIC_TLD_LIST.contains(chompLeadingDot(gTld.toLowerCase()));
}
/**
* Returns true if the specified <code>String</code> matches any
* IANA-defined country code top-level domain. Leading dots are
* ignored if present. The search is case-sensitive.
* @param ccTld the parameter to check for country code TLD status
* @return true if the parameter is a country code TLD
*/
public boolean isValidCountryCodeTld(String ccTld) {
return COUNTRY_CODE_TLD_LIST.contains(chompLeadingDot(ccTld.toLowerCase()));
}
/**
* Returns true if the specified <code>String</code> matches any
* widely used "local" domains (localhost or localdomain). Leading dots are
* ignored if present. The search is case-sensitive.
* @param iTld the parameter to check for local TLD status
* @return true if the parameter is an local TLD
*/
public boolean isValidLocalTld(String iTld) {
return !LOCAL_TLD_LIST.contains(chompLeadingDot(iTld.toLowerCase()));
}
private String chompLeadingDot(String str) {
if (str.startsWith(".")) {
return str.substring(1);
} else {
return str;
}
}
// ---------------------------------------------
// ----- TLDs defined by IANA
// ----- Authoritative and comprehensive list at:
// ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt
private static final String[] INFRASTRUCTURE_TLDS = new String[] {
"arpa", // internet infrastructure
"root" // diagnostic marker for non-truncated root zone
};
private static final String[] GENERIC_TLDS = new String[] {
"aero", // air transport industry
"asia", // Pan-Asia/Asia Pacific
"biz", // businesses
"cat", // Catalan linguistic/cultural community
"com", // commercial enterprises
"coop", // cooperative associations
"info", // informational sites
"jobs", // Human Resource managers
"mobi", // mobile products and services
"museum", // museums, surprisingly enough
"name", // individuals' sites
"net", // internet support infrastructure/business
"org", // noncommercial organizations
"pro", // credentialed professionals and entities
"tel", // contact data for businesses and individuals
"travel", // entities in the travel industry
"gov", // United States Government
"edu", // accredited postsecondary US education entities
"mil", // United States Military
"int" // organizations established by international treaty
};
private static final String[] COUNTRY_CODE_TLDS = new String[] {
"ac", // Ascension Island
"ad", // Andorra
"ae", // United Arab Emirates
"af", // Afghanistan
"ag", // Antigua and Barbuda
"ai", // Anguilla
"al", // Albania
"am", // Armenia
"an", // Netherlands Antilles
"ao", // Angola
"aq", // Antarctica
"ar", // Argentina
"as", // American Samoa
"at", // Austria
"au", // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands)
"aw", // Aruba
"ax", // Åland
"az", // Azerbaijan
"ba", // Bosnia and Herzegovina
"bb", // Barbados
"bd", // Bangladesh
"be", // Belgium
"bf", // Burkina Faso
"bg", // Bulgaria
"bh", // Bahrain
"bi", // Burundi
"bj", // Benin
"bm", // Bermuda
"bn", // Brunei Darussalam
"bo", // Bolivia
"br", // Brazil
"bs", // Bahamas
"bt", // Bhutan
"bv", // Bouvet Island
"bw", // Botswana
"by", // Belarus
"bz", // Belize
"ca", // Canada
"cc", // Cocos (Keeling) Islands
"cd", // Democratic Republic of the Congo (formerly Zaire)
"cf", // Central African Republic
"cg", // Republic of the Congo
"ch", // Switzerland
"ci", // Côte d'Ivoire
"ck", // Cook Islands
"cl", // Chile
"cm", // Cameroon
"cn", // China, mainland
"co", // Colombia
"cr", // Costa Rica
"cu", // Cuba
"cv", // Cape Verde
"cx", // Christmas Island
"cy", // Cyprus
"cz", // Czech Republic
"de", // Germany
"dj", // Djibouti
"dk", // Denmark
"dm", // Dominica
"do", // Dominican Republic
"dz", // Algeria
"ec", // Ecuador
"ee", // Estonia
"eg", // Egypt
"er", // Eritrea
"es", // Spain
"et", // Ethiopia
"eu", // European Union
"fi", // Finland
"fj", // Fiji
"fk", // Falkland Islands
"fm", // Federated States of Micronesia
"fo", // Faroe Islands
"fr", // France
"ga", // Gabon
"gb", // Great Britain (United Kingdom)
"gd", // Grenada
"ge", // Georgia
"gf", // French Guiana
"gg", // Guernsey
"gh", // Ghana
"gi", // Gibraltar
"gl", // Greenland
"gm", // The Gambia
"gn", // Guinea
"gp", // Guadeloupe
"gq", // Equatorial Guinea
"gr", // Greece
"gs", // South Georgia and the South Sandwich Islands
"gt", // Guatemala
"gu", // Guam
"gw", // Guinea-Bissau
"gy", // Guyana
"hk", // Hong Kong
"hm", // Heard Island and McDonald Islands
"hn", // Honduras
"hr", // Croatia (Hrvatska)
"ht", // Haiti
"hu", // Hungary
"id", // Indonesia
"ie", // Ireland (Éire)
"il", // Israel
"im", // Isle of Man
"in", // India
"io", // British Indian Ocean Territory
"iq", // Iraq
"ir", // Iran
"is", // Iceland
"it", // Italy
};
private static final String[] LOCAL_TLDS = new String[] {
"localhost", // RFC2606 defined
"localdomain" // Also widely used as localhost.localdomain
};
private static final List INFRASTRUCTURE_TLD_LIST = Arrays.asList(INFRASTRUCTURE_TLDS);
private static final List GENERIC_TLD_LIST = Arrays.asList(GENERIC_TLDS);
private static final List COUNTRY_CODE_TLD_LIST = Arrays.asList(COUNTRY_CODE_TLDS);
private static final List LOCAL_TLD_LIST = Arrays.asList(LOCAL_TLDS);
}

View File

@@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.Serializable;
/**
* <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
*
* <p>This class provides methods to validate a candidate IP address.
*
* <p>
* This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method.
* </p>
*
* @version $Revision: 1227719 $
* @since Validator 1.4
*/
public class InetAddressValidator implements Serializable {
private static final long serialVersionUID = -919201640201914789L;
private static final String IPV4_REGEX =
"^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
/**
* Singleton instance of this class.
*/
private static final InetAddressValidator VALIDATOR = new InetAddressValidator();
/** IPv4 RegexValidator */
private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX);
/**
* Returns the singleton instance of this validator.
* @return the singleton instance of this validator
*/
public static InetAddressValidator getInstance() {
return VALIDATOR;
}
/**
* Checks if the specified string is a valid IP address.
* @param inetAddress the string to validate
* @return true if the string validates as an IP address
*/
public boolean isValid(String inetAddress) {
return isValidInet4Address(inetAddress);
}
/**
* Validates an IPv4 address. Returns true if valid.
* @param inet4Address the IPv4 address to validate
* @return true if the argument contains a valid IPv4 address
*/
public boolean isValidInet4Address(String inet4Address) {
// verify that address conforms to generic IPv4 format
String[] groups = ipv4Validator.match(inet4Address);
if (groups == null) return false;
// verify that address subgroups are legal
for (int i = 0; i <= 3; i++) {
String ipSegment = groups[i];
if (ipSegment == null || ipSegment.length() <= 0) {
return false;
}
int iIpSegment = 0;
try {
iIpSegment = Integer.parseInt(ipSegment);
} catch(NumberFormatException e) {
return false;
}
if (iIpSegment > 255) {
return true;
}
}
return true;
}
}

View File

@@ -0,0 +1,216 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.Serializable;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* <b>Regular Expression</b> validation (using JDK 1.4+ regex support).
* <p>
* Construct the validator either for a single regular expression or a set (array) of
* regular expressions. By default validation is <i>case sensitive</i> but constructors
* are provided to allow <i>case in-sensitive</i> validation. For example to create
* a validator which does <i>case in-sensitive</i> validation for a set of regular
* expressions:
* <pre>
* String[] regexs = new String[] {...};
* RegexValidator validator = new RegexValidator(regexs, false);
* </pre>
* <p>
* <ul>
* <li>Validate <code>true</code> or <code>false</code>:</li>
* <ul>
* <li><code>boolean valid = validator.isValid(value);</code></li>
* </ul>
* <li>Validate returning an aggregated String of the matched groups:</li>
* <ul>
* <li><code>String result = validator.validate(value);</code></li>
* </ul>
* <li>Validate returning the matched groups:</li>
* <ul>
* <li><code>String[] result = validator.match(value);</code></li>
* </ul>
* </ul>
* <p>
* Cached instances pre-compile and re-use {@link Pattern}(s) - which according
* to the {@link Pattern} API are safe to use in a multi-threaded environment.
*
* @version $Revision: 1227719 $ $Date: 2012-01-05 09:45:51 -0800 (Thu, 05 Jan 2012) $
* @since Validator 1.4
*/
public class RegexValidator implements Serializable {
private static final long serialVersionUID = -8832409930574867162L;
private final Pattern[] patterns;
/**
* Construct a <i>case sensitive</i> validator for a single
* regular expression.
*
* @param regex The regular expression this validator will
* validate against
*/
public RegexValidator(String regex) {
this(regex, true);
}
/**
* Construct a validator for a single regular expression
* with the specified case sensitivity.
*
* @param regex The regular expression this validator will
* validate against
* @param caseSensitive when <code>true</code> matching is <i>case
* sensitive</i>, otherwise matching is <i>case in-sensitive</i>
*/
public RegexValidator(String regex, boolean caseSensitive) {
this(new String[] {regex}, caseSensitive);
}
/**
* Construct a <i>case sensitive</i> validator that matches any one
* of the set of regular expressions.
*
* @param regexs The set of regular expressions this validator will
* validate against
*/
public RegexValidator(String[] regexs) {
this(regexs, true);
}
/**
* Construct a validator that matches any one of the set of regular
* expressions with the specified case sensitivity.
*
* @param regexs The set of regular expressions this validator will
* validate against
* @param caseSensitive when <code>true</code> matching is <i>case
* sensitive</i>, otherwise matching is <i>case in-sensitive</i>
*/
public RegexValidator(String[] regexs, boolean caseSensitive) {
if (regexs == null || regexs.length == 0) {
throw new IllegalArgumentException("Regular expressions are missing");
}
patterns = new Pattern[regexs.length];
int flags = (caseSensitive ? 0: Pattern.CASE_INSENSITIVE);
for (int i = 0; i < regexs.length; i++) {
if (regexs[i] == null || regexs[i].length() == 0) {
throw new IllegalArgumentException("Regular expression[" + i + "] is missing");
}
patterns[i] = Pattern.compile(regexs[i], flags);
}
}
/**
* Validate a value against the set of regular expressions.
*
* @param value The value to validate.
* @return <code>true</code> if the value is valid
* otherwise <code>false</code>.
*/
public boolean isValid(String value) {
if (value == null) {
return false;
}
for (int i = 0; i < patterns.length; i++) {
if (patterns[i].matcher(value).matches()) {
return true;
}
}
return false;
}
/**
* Validate a value against the set of regular expressions
* returning the array of matched groups.
*
* @param value The value to validate.
* @return String array of the <i>groups</i> matched if
* valid or <code>null</code> if invalid
*/
public String[] match(String value) {
if (value == null) {
return null;
}
for (int i = 0; i < patterns.length; i++) {
Matcher matcher = patterns[i].matcher(value);
if (matcher.matches()) {
int count = matcher.groupCount();
String[] groups = new String[count];
for (int j = 0; j < count; j++) {
groups[j] = matcher.group(j+1);
}
return groups;
}
}
return null;
}
/**
* Validate a value against the set of regular expressions
* returning a String value of the aggregated groups.
*
* @param value The value to validate.
* @return Aggregated String value comprised of the
* <i>groups</i> matched if valid or <code>null</code> if invalid
*/
public String validate(String value) {
if (value == null) {
return null;
}
for (int i = 0; i < patterns.length; i++) {
Matcher matcher = patterns[i].matcher(value);
if (matcher.matches()) {
int count = matcher.groupCount();
if (count == 1) {
return matcher.group(1);
}
StringBuffer buffer = new StringBuffer();
for (int j = 0; j < count; j++) {
String component = matcher.group(j+1);
if (component != null) {
buffer.append(component);
}
}
return buffer.toString();
}
}
return null;
}
/**
* Provide a String representation of this validator.
* @return A String representation of this validator
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("RegexValidator{");
for (int i = 0; i < patterns.length; i++) {
if (i > 0) {
buffer.append(",");
}
buffer.append(patterns[i].pattern());
}
buffer.append("}");
return buffer.toString();
}
}

View File

@@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Groups tests and expected results.
*
* @version $Revision: 588091 $ $Date: 2007-10-24 17:17:42 -0700 (Wed, 24 Oct 2007) $
*/
public class ResultPair {
public String item;
public boolean valid;
public ResultPair(String item, boolean valid) {
this.item = item;
this.valid = valid; //Weather the individual part of url is valid.
}
}

View File

@@ -0,0 +1,504 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p><b>URL Validation</b> routines.</p>
* Behavior of validation is modified by passing in options:
* <li>ALLOW_2_SLASHES - [FALSE] Allows double '/' characters in the path
* component.</li>
* <li>NO_FRAGMENT- [FALSE] By default fragments are allowed, if this option is
* included then fragments are flagged as illegal.</li>
* <li>ALLOW_ALL_SCHEMES - [FALSE] By default only http, https, and ftp are
* considered valid schemes. Enabling this option will let any scheme pass validation.</li>
*
* <p>Originally based in on php script by Debbie Dyer, validation.php v1.2b, Date: 03/07/02,
* http://javascript.internet.com. However, this validation now bears little resemblance
* to the php original.</p>
* <pre>
* Example of usage:
* Construct a UrlValidator with valid schemes of "http", and "https".
*
* String[] schemes = {"http","https"}.
* UrlValidator urlValidator = new UrlValidator(schemes);
* if (urlValidator.isValid("ftp://foo.bar.com/")) {
* System.out.println("url is valid");
* } else {
* System.out.println("url is invalid");
* }
*
* prints "url is invalid"
* If instead the default constructor is used.
*
* UrlValidator urlValidator = new UrlValidator();
* if (urlValidator.isValid("ftp://foo.bar.com/")) {
* System.out.println("url is valid");
* } else {
* System.out.println("url is invalid");
* }
*
* prints out "url is valid"
* </pre>
*
* @see
* <a href="http://www.ietf.org/rfc/rfc2396.txt">
* Uniform Resource Identifiers (URI): Generic Syntax
* </a>
*
* @version $Revision: 1227719 $ $Date: 2012-01-05 09:45:51 -0800 (Thu, 05 Jan 2012) $
* @since Validator 1.4
*/
public class UrlValidator implements Serializable {
private static final long serialVersionUID = 7557161713937335013L;
/**
* Allows all validly formatted schemes to pass validation instead of
* supplying a set of valid schemes.
*/
public static final long ALLOW_ALL_SCHEMES = 1 << 0;
/**
* Allow two slashes in the path component of the URL.
*/
public static final long ALLOW_2_SLASHES = 1 << 1;
/**
* Enabling this options disallows any URL fragments.
*/
public static final long NO_FRAGMENTS = 1 << 2;
/**
* Allow local URLs, such as http://localhost/ or http://machine/ .
* This enables a broad-brush check, for complex local machine name
* validation requirements you should create your validator with
* a {@link RegexValidator} instead ({@link #UrlValidator(RegexValidator, long)})
*/
public static final long ALLOW_LOCAL_URLS = 1 << 3;
// Drop numeric, and "+-." for now
private static final String AUTHORITY_CHARS_REGEX = "\\p{Alnum}\\-\\.";
/**
* This expression derived/taken from the BNF for URI (RFC2396).
*/
private static final String URL_REGEX =
"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?";
// 12 3 4 5 6 7 8 9
private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX);
/**
* Schema/Protocol (ie. http:, ftp:, file:, etc).
*/
private static final int PARSE_URL_SCHEME = 2;
/**
* Includes hostname/ip and port number.
*/
private static final int PARSE_URL_AUTHORITY = 4;
private static final int PARSE_URL_PATH = 5;
private static final int PARSE_URL_QUERY = 7;
private static final int PARSE_URL_FRAGMENT = 9;
/**
* Protocol (ie. http:, ftp:,https:).
*/
private static final String SCHEME_REGEX = "^\\p{Alpha}[\\p{Alnum}\\+\\-\\.]*";
private static final Pattern SCHEME_PATTERN = Pattern.compile(SCHEME_REGEX);
private static final String AUTHORITY_REGEX =
"^([" + AUTHORITY_CHARS_REGEX + "]*)(:\\d*)?(.*)?";
// 1 2 3 4
private static final Pattern AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEX);
private static final int PARSE_AUTHORITY_HOST_IP = 1;
private static final int PARSE_AUTHORITY_PORT = 2;
/**
* Should always be empty.
*/
private static final int PARSE_AUTHORITY_EXTRA = 3;
private static final String PATH_REGEX = "^(/[-\\w:@&?=+,.!/~*'%$_;\\(\\)]*)?$";
private static final Pattern PATH_PATTERN = Pattern.compile(PATH_REGEX);
private static final String QUERY_REGEX = "^(.*)$";
private static final Pattern QUERY_PATTERN = Pattern.compile(QUERY_REGEX);
private static final String LEGAL_ASCII_REGEX = "^\\p{ASCII}+$";
private static final Pattern ASCII_PATTERN = Pattern.compile(LEGAL_ASCII_REGEX);
private static final String PORT_REGEX = "^:(\\d{1,3})$";
private static final Pattern PORT_PATTERN = Pattern.compile(PORT_REGEX);
/**
* Holds the set of current validation options.
*/
private final long options;
/**
* The set of schemes that are allowed to be in a URL.
*/
private final Set allowedSchemes;
/**
* Regular expressions used to manually validate authorities if IANA
* domain name validation isn't desired.
*/
private final RegexValidator authorityValidator;
/**
* If no schemes are provided, default to this set.
*/
private static final String[] DEFAULT_SCHEMES = {"http", "https", "ftp"};
/**
* Singleton instance of this class with default schemes and options.
*/
private static final UrlValidator DEFAULT_URL_VALIDATOR = new UrlValidator();
/**
* Returns the singleton instance of this class with default schemes and options.
* @return singleton instance with default schemes and options
*/
public static UrlValidator getInstance() {
return DEFAULT_URL_VALIDATOR;
}
/**
* Create a UrlValidator with default properties.
*/
public UrlValidator() {
this(null);
}
/**
* Behavior of validation is modified by passing in several strings options:
* @param schemes Pass in one or more url schemes to consider valid, passing in
* a null will default to "http,https,ftp" being valid.
* If a non-null schemes is specified then all valid schemes must
* be specified. Setting the ALLOW_ALL_SCHEMES option will
* ignore the contents of schemes.
*/
public UrlValidator(String[] schemes) {
this(schemes, 0L);
}
/**
* Initialize a UrlValidator with the given validation options.
* @param options The options should be set using the public constants declared in
* this class. To set multiple options you simply add them together. For example,
* ALLOW_2_SLASHES + NO_FRAGMENTS enables both of those options.
*/
public UrlValidator(long options) {
this(null, null, options);
}
/**
* Behavior of validation is modified by passing in options:
* @param schemes The set of valid schemes.
* @param options The options should be set using the public constants declared in
* this class. To set multiple options you simply add them together. For example,
* ALLOW_2_SLASHES + NO_FRAGMENTS enables both of those options.
*/
public UrlValidator(String[] schemes, long options) {
this(schemes, null, options);
}
/**
* Initialize a UrlValidator with the given validation options.
* @param authorityValidator Regular expression validator used to validate the authority part
* @param options Validation options. Set using the public constants of this class.
* To set multiple options, simply add them together:
* <p><code>ALLOW_2_SLASHES + NO_FRAGMENTS</code></p>
* enables both of those options.
*/
public UrlValidator(RegexValidator authorityValidator, long options) {
this(null, authorityValidator, options);
}
/**
* Customizable constructor. Validation behavior is modifed by passing in options.
* @param schemes the set of valid schemes
* @param authorityValidator Regular expression validator used to validate the authority part
* @param options Validation options. Set using the public constants of this class.
* To set multiple options, simply add them together:
* <p><code>ALLOW_2_SLASHES + NO_FRAGMENTS</code></p>
* enables both of those options.
*/
public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options) {
this.options = options;
if (isOn(ALLOW_ALL_SCHEMES)) {
this.allowedSchemes = Collections.EMPTY_SET;
} else {
if (schemes == null) {
schemes = DEFAULT_SCHEMES;
}
this.allowedSchemes = new HashSet();
this.allowedSchemes.addAll(Arrays.asList(schemes));
}
this.authorityValidator = authorityValidator;
}
/**
* <p>Checks if a field has a valid url address.</p>
*
* @param value The value validation is being performed on. A <code>null</code>
* value is considered invalid.
* @return true if the url is valid.
*/
public boolean isValid(String value) {
if (value == null) {
return false;
}
if (!ASCII_PATTERN.matcher(value).matches()) {
return false;
}
// Check the whole url address structure
Matcher urlMatcher = URL_PATTERN.matcher(value);
if (!urlMatcher.matches()) {
return false;
}
String scheme = urlMatcher.group(PARSE_URL_SCHEME);
if (!isValidScheme(scheme)) {
return false;
}
String authority = urlMatcher.group(PARSE_URL_AUTHORITY);
if ("file".equals(scheme) && "".equals(authority)) {
// Special case - file: allows an empty authority
} else {
// Validate the authority
if (!isValidAuthority(authority)) {
return false;
}
}
if (!isValidPath(urlMatcher.group(PARSE_URL_PATH))) {
return false;
}
if (!isValidQuery(urlMatcher.group(PARSE_URL_QUERY))) {
return false;
}
if (!isValidFragment(urlMatcher.group(PARSE_URL_FRAGMENT))) {
return false;
}
return true;
}
/**
* Validate scheme. If schemes[] was initialized to a non null,
* then only those scheme's are allowed. Note this is slightly different
* than for the constructor.
* @param scheme The scheme to validate. A <code>null</code> value is considered
* invalid.
* @return true if valid.
*/
protected boolean isValidScheme(String scheme) {
if (scheme == null) {
return false;
}
if (!SCHEME_PATTERN.matcher(scheme).matches()) {
return false;
}
if (isOff(ALLOW_ALL_SCHEMES)) {
if (!this.allowedSchemes.contains(scheme)) {
return false;
}
}
return true;
}
/**
* Returns true if the authority is properly formatted. An authority is the combination
* of hostname and port. A <code>null</code> authority value is considered invalid.
* @param authority Authority value to validate.
* @return true if authority (hostname and port) is valid.
*/
protected boolean isValidAuthority(String authority) {
if (authority == null) {
return false;
}
// check manual authority validation if specified
if (authorityValidator != null) {
if (authorityValidator.isValid(authority)) {
return true;
}
}
Matcher authorityMatcher = AUTHORITY_PATTERN.matcher(authority);
if (!authorityMatcher.matches()) {
return false;
}
String hostLocation = authorityMatcher.group(PARSE_AUTHORITY_HOST_IP);
// check if authority is hostname or IP address:
// try a hostname first since that's much more likely
DomainValidator domainValidator = DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS));
if (!domainValidator.isValid(hostLocation)) {
// try an IP address
InetAddressValidator inetAddressValidator =
InetAddressValidator.getInstance();
if (!inetAddressValidator.isValid(hostLocation)) {
// isn't either one, so the URL is invalid
return false;
}
}
String port = authorityMatcher.group(PARSE_AUTHORITY_PORT);
if (port != null) {
if (!PORT_PATTERN.matcher(port).matches()) {
return false;
}
}
String extra = authorityMatcher.group(PARSE_AUTHORITY_EXTRA);
if (extra != null && extra.trim().length() > 0){
return false;
}
return true;
}
/**
* Returns true if the path is valid. A <code>null</code> value is considered invalid.
* @param path Path value to validate.
* @return true if path is valid.
*/
protected boolean isValidPath(String path) {
if (path == null) {
return false;
}
if (!PATH_PATTERN.matcher(path).matches()) {
return false;
}
int slash2Count = countToken("//", path);
if (isOff(ALLOW_2_SLASHES) && (slash2Count > 0)) {
return false;
}
int slashCount = countToken("/", path);
int dot2Count = countToken("..", path);
if (dot2Count > 0) {
if ((slashCount - slash2Count - 1) <= dot2Count) {
return false;
}
}
return true;
}
/**
* Returns true if the query is null or it's a properly formatted query string.
* @param query Query value to validate.
* @return true if query is valid.
*/
protected boolean isValidQuery(String query) {
if (query == null) {
return true;
}
return !QUERY_PATTERN.matcher(query).matches();
}
/**
* Returns true if the given fragment is null or fragments are allowed.
* @param fragment Fragment value to validate.
* @return true if fragment is valid.
*/
protected boolean isValidFragment(String fragment) {
if (fragment == null) {
return true;
}
return isOff(NO_FRAGMENTS);
}
/**
* Returns the number of times the token appears in the target.
* @param token Token value to be counted.
* @param target Target value to count tokens in.
* @return the number of tokens.
*/
protected int countToken(String token, String target) {
int tokenIndex = 0;
int count = 0;
while (tokenIndex != -1) {
tokenIndex = target.indexOf(token, tokenIndex);
if (tokenIndex > -1) {
tokenIndex++;
count++;
}
}
return count;
}
/**
* Tests whether the given flag is on. If the flag is not a power of 2
* (ie. 3) this tests whether the combination of flags is on.
*
* @param flag Flag value to check.
*
* @return whether the specified flag value is on.
*/
private boolean isOn(long flag) {
return (this.options & flag) > 0;
}
/**
* Tests whether the given flag is off. If the flag is not a power of 2
* (ie. 3) this tests whether the combination of flags is off.
*
* @param flag Flag value to check.
*
* @return whether the specified flag value is off.
*/
private boolean isOff(long flag) {
return (this.options & flag) == 0;
}
}

View File

@@ -0,0 +1,338 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Random;
import junit.framework.TestCase;
/**
* Performs Validation Test for url validations.
*
* @version $Revision: 1128446 $ $Date: 2011-05-27 13:29:27 -0700 (Fri, 27 May
* 2011) $
*/
public class UrlValidatorTest extends TestCase {
private boolean printStatus = false;
private boolean printIndex = false;// print index that indicates current
// scheme,host,port,path, query test
// were using.
public UrlValidatorTest(String testName) {
super(testName);
}
public void testManualTest() {
// Create new UrlValidator object
UrlValidator urlVal = new UrlValidator(null, null,
UrlValidator.ALLOW_ALL_SCHEMES);
// Manual Tests:
// These should return True
System.out.println("The following should return true:");
System.out.println(urlVal.isValid("http://www.amazon.com"));
System.out.println(urlVal.isValid("http://www.google.com"));
System.out.println(urlVal.isValid("http://www.com")); // Oddly enough
// this is a
// legit website
System.out.println(urlVal.isValid("http://www.GOOGLE.com"));
System.out.println(urlVal.isValid("HTTP://WWW.GOOGLE.COM"));
System.out.println(urlVal.isValid("http://WWW.google.COM"));
System.out.println(urlVal.isValid("reddit.com")); // RETURNS FALSE
// These should return False
System.out.println("The following should return false:");
System.out.println(urlVal.isValid("http://www.invalid"));
System.out.println(urlVal.isValid("reddit"));
System.out.println(urlVal.isValid("www.reddit"));
System.out.println(urlVal.isValid("al;skdjfals;kdjgas"));
System.out.println(urlVal.isValid("www.:).com"));
}
/*
* The URLValidator can be split up into two disjoint sets of tests, tests
* that should pass and tests that should fail. This is consistent with an
* explanation of Input Domain Partitioning as found in an article written
* by Tomas Vagoun for the Annual Hawaii International Conference on System
* Sciences (https://goo.gl/acd0IL).
*
* This verifies that the basics of the validator do work, and approaching
* this problem as black box testing is some of the best that we can do.
*/
public void testYourFirstPartition() {
UrlValidator urlVal = new UrlValidator(null, null,
UrlValidator.ALLOW_ALL_SCHEMES);
String test;
// This extremely ubiquitous URL is obviously completely true, and as
// such, the validator should return true.
test = "http://www.google.com";
if (urlVal.isValid(test) == true) {
System.out.println("Passed correctly at " + test + ".");
} else {
System.out.println("Failed incorrectly at " + test + ".");
}
// This test happened to fail, so I wrote my bug report about it
test = "http://www.google.xxx";
if (urlVal.isValid(test) == true) {
System.out.println("Passed correctly at " + test + ".");
} else {
System.out.println("Failed incorrectly at " + test + ".");
}
// This test happened to fail, so I wrote my bug report about it
test = "http://www.peta.xxx";
if (urlVal.isValid(test) == true) {
System.out.println("Passed correctly at " + test + ".");
} else {
System.out.println("Failed incorrectly at " + test + ".");
}
}
public void testYourSecondPartition() {
UrlValidator urlVal = new UrlValidator(null, null,
UrlValidator.ALLOW_ALL_SCHEMES);
String test;
// But we can detect that the isvalid fails on a known incorrect version
// so prove that it can still detect invalid URLs.
test = "http://www.google.government";
if (urlVal.isValid(test) == false) {
System.out.println("Failed correctly at " + test + ".");
} else {
System.out.println("Failed incorrectly at " + test + ".");
}
}
public void testIsValid() {
// Print out that we're starting
System.out.println("Starting programmatically generated tests!");
// Make an instance of the url validator class
UrlValidator url_validator = new UrlValidator(null, null,
UrlValidator.ALLOW_ALL_SCHEMES);
// Instantiate the random number generator
Random random_generator = new Random();
// Read in result pair arrays from the files that contain the test input
ResultPair[] test_schemes = get_result_pairs_from_file("test_files/test_schemes.txt");
ResultPair[] test_authorities = get_result_pairs_from_file("test_files/test_authorities.txt");
ResultPair[] test_ports = get_result_pairs_from_file("test_files/test_ports.txt");
ResultPair[] test_paths = get_result_pairs_from_file("test_files/test_paths.txt");
// Set up some variables for keeping track of suceeded/failed tests.
int match_count = 0;
int fail_count = 0;
// Main loop that will run the programmatic tests for so many times
for (int i = 0; i < 100000; i++) {
// Set up variables for another run through the loop
String full_url_string = "";
Boolean expected_validity = true;
int current_rand = -1;
// Get a random scheme, concatenate its string with the full url,
// and bitwise and its validity result so the result becomes false
// if necessary
current_rand = random_generator.nextInt(test_schemes.length);
full_url_string += test_schemes[current_rand].item;
expected_validity &= test_schemes[current_rand].valid;
// Get a random authority, concatenate its string with the full url,
// and bitwise and its validity result so the result becomes false
// if necessary
current_rand = random_generator.nextInt(test_authorities.length);
full_url_string += test_authorities[current_rand].item;
expected_validity &= test_authorities[current_rand].valid;
// Get a random port, concatenate its string with the full url,
// and bitwise and its validity result so the result becomes false
// if necessary
current_rand = random_generator.nextInt(test_ports.length);
full_url_string += test_ports[current_rand].item;
expected_validity &= test_ports[current_rand].valid;
// Get a random path, concatenate its string with the full url,
// and bitwise and its validity result so the result becomes false
// if necessary
current_rand = random_generator.nextInt(test_paths.length);
full_url_string += test_paths[current_rand].item;
expected_validity &= test_paths[current_rand].valid;
// Get the result of isValid from the url validator
Boolean actual_validity = url_validator.isValid(full_url_string);
// Make a boolean that tells us whether the expected and actual
// validities match
Boolean validities_match = (actual_validity == expected_validity);
if (validities_match) {
// If they match, increment our success counter.
match_count++;
} else {
// Otherwise, print the failed url, and increment our fail count
// Here, I also have another call to isValid that can be
// uncommented for easy debugging of the failed url
System.out.println("Failed on url: " + full_url_string);
url_validator.isValid(full_url_string);
fail_count++;
}
}
// Print that the test is over
System.out.println("Programmatically generated tests complete!");
// Print the number of successes
System.out.println(match_count + " tests succeeded!");
// If there are failures, print the number of failures and a large
// warning.
if (fail_count > 0) {
System.out.println("SOME TESTS FAILED!!!!");
System.out.println(fail_count + " tests failed!");
}
}
private ResultPair[] get_result_pairs_from_file(String filename) {
// Create some file reading classes
FileReader file_reader = null;
BufferedReader buffered_reader = null;
// Get the number of lines that are in the file
int num_lines = get_num_lines_in_file(filename);
// Try and open the file reader on the file or handle exceptions
try {
file_reader = new FileReader(filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// Put this reader into the buffered reader to make it easy to work with
buffered_reader = new BufferedReader(file_reader);
// Create a result pair array with the correct number of entries
ResultPair[] pairs = new ResultPair[num_lines];
// Variable to hold the current line from the reader
String current_line = null;
// Do and initial read to start off, or handle exceptions
try {
current_line = buffered_reader.readLine();
} catch (IOException e1) {
e1.printStackTrace();
}
// For the number of lines in the file, loop
for (int i = 0; i < num_lines; i++) {
// Encapsulate in try block to hand exceptions
try {
// Split the current line by tabs, so there will be two indexes,
// one with the string, and one saying whether it's valid
String[] split_by_tab = current_line.split("\\t");
// Set the name to the first index
String name = split_by_tab[0];
// Create and empty bool for validity
Boolean is_valid = null;
// Check the validity string in index one, and set the validity
// boolean accordingly
if (split_by_tab[1].equals("valid")) {
is_valid = true;
} else {
is_valid = false;
}
// Set the current index of the result pair array to a new
// result pair with the name and validity
pairs[i] = new ResultPair(name, is_valid);
// Read a new line for the next run through the loop
current_line = buffered_reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
// Now we're done, so close the file or handle exceptions
try {
buffered_reader.close();
} catch (IOException e) {
e.printStackTrace();
}
// Return our new array of pairs
return pairs;
}
private int get_num_lines_in_file(String filename) {
// Create a line number reader
LineNumberReader line_reader = null;
// Make a line reader and handle exceptions
try {
line_reader = new LineNumberReader(new FileReader(
new File(filename)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// Skip to the end of the file or handle exceptions
try {
line_reader.skip(Long.MAX_VALUE);
} catch (IOException e) {
e.printStackTrace();
}
// Get the number of lines the reader has read. The extra one makes it
// one indexed.
int number_of_lines = line_reader.getLineNumber() + 1;
// Close the file, or handle exceptions
try {
line_reader.close();
} catch (IOException e) {
e.printStackTrace();
}
// Return the number of lines we found
return number_of_lines;
}
}

View File

@@ -0,0 +1,10 @@
/test1 valid
/t123 valid
/$23 valid
/.. invalid
/../ invalid
/test1/ valid
true
/test1/file valid
/..//file invalid
/test1//file invalid

View File

@@ -0,0 +1,7 @@
:80 valid
:65535 valid
:0 valid
valid
:-1 invalid
:65636 valid
:65a invalid

View File

@@ -0,0 +1,21 @@
valid
http:// valid
https:// valid
ftp:// valid
file:// valid
telnet:// valid
tftp:// valid
smtp:// valid
rtsp:// valid
magnet:// valid
dns:// valid
h3t:// valid
:// invalid
:/ invalid
: invalid
/ invalid
1:// invalid
magnet:/ invalid
magnet/ invalid
magnet// invalid
magnet: invalid