// Returns the number of direct connections between two nodes // Also fills the type[] array with the various connection types // Returns 0 if no direct connection (i.e. not adjacent in graph) int connections(Map g, LocationID start, LocationID end, TransportID type[]) { int numConnections = 0; assert(g != NULL); if (!validPlace(start) || !validPlace(end)) { fprintf(stderr, "Map.c: connections(): start location(%d) || end locations(%d) not valid", start, end); return numConnections; } VList curr = g->connections[start]; while (curr != NULL) { // Check for regular connections if (curr->v == end) { type[numConnections] = curr->type; numConnections++; // Check for connections via a common sea } else if (idToType(curr->v) == SEA) { VList endCurr = g->connections[end]; while (endCurr != NULL) { if (endCurr->v == curr->v) { type[numConnections] = curr->type; numConnections++; } endCurr = endCurr->next; } } curr = curr->next; } return numConnections; }
/* This function returns true if input locations have a connection of type and false if there is no connection */ int haveConnectionByType (Map g, LocationID from, LocationID to, TransportID type, PlayerID player) { if (!validPlace(from) || !validPlace(to)) { fprintf(stderr, "Map.c: haveConnectionByType(): Invalid location input: %d ----> %d", from, to); return 0; } VList curr; for (curr = g->connections[from]; curr->next != NULL; curr = curr->next) if (curr->v == to && (curr->type == type || (type == ANY && player != PLAYER_DRACULA))) return 1; if (curr->v == to && (curr->type == type || (type == ANY && player != PLAYER_DRACULA))) return 1; return 0; }
/* Input: a locationID, transportID and a Map Returns: the input pointer to an array with all adjacent nodes connected by type transportID */ LocationID *allAdjacent (Map g, LocationID loc, TransportID type, LocationID *adjacent) { #ifdef DEBUG if (g == NULL) { fprintf(stderr, "Map.c:allAdjacent(): Map input == NULL"); abort(); } if (!validPlace(loc)) { fprintf(stderr, "Map.c: allAdjacent(): Invalid location input"); abort(); } #else if (loc < MIN_MAP_LOCATION || loc > MAX_MAP_LOCATION) return adjacent; #endif // traverse list[loc] and find all locations of transportID type VList curr = g->connections[loc]; int i = 0; do { if (curr->type == type) { adjacent[i] = curr->v; i++; } curr = curr->next; } while (curr != NULL); return adjacent; }
LocationID *connectedLocations(GameView currentView, int *numLocations, LocationID from, PlayerID player, Round round, int road, int rail, int sea) { assert(currentView != NULL); assert(numLocations != NULL); assert(validPlace(from)); assert(0 <= player && player < NUM_PLAYERS); int i, j, k; // our map Map ourMap = newMap(); // boolean array storing whether or not we can reach each vertex int canReach[NUM_MAP_LOCATIONS]; // initialise it for(i=0;i<NUM_MAP_LOCATIONS;i++) { canReach[i] = FALSE; } // remember we can always reach ourselves canReach[from] = TRUE; // pairwise shortest rail distances int railDist[NUM_MAP_LOCATIONS][NUM_MAP_LOCATIONS]; // floyd warshall to calculate pariwse shortest paths. // initialise distances for(i=0;i<NUM_MAP_LOCATIONS;i++) { for(j=0;j<NUM_MAP_LOCATIONS;j++) { if(i == j) { railDist[i][j] = 0; } else { int edgeDist = getDist(ourMap, RAIL, i, j); if(edgeDist == NO_EDGE) { edgeDist = INFINITY; } railDist[i][j] = edgeDist; } } } // floyd warshall time! for(j=0;j<NUM_MAP_LOCATIONS;j++) { for(i=0;i<NUM_MAP_LOCATIONS;i++) { for(k=0;k<NUM_MAP_LOCATIONS;k++) { railDist[i][k] = min(railDist[i][k], railDist[i][j] + railDist[j][k]); } } } // actually do the thing // invoke special rules: transform road, rail and sea not just to store // TRUE/FALSE but to store the maximum number of edges of that type we can // move in if(rail == TRUE) { if(player == PLAYER_DRACULA) { // dracula can't move by rail rail = 0; } else { // determine how far hunters can move by rail rail = (round+player) % RAIL_RESTRICT; } } else { rail = 0; } // D("rail=%d\n",rail); // add rail links for(i=0;i<NUM_MAP_LOCATIONS;i++) { if(railDist[from][i] <= rail) { canReach[i] = TRUE; } } if(road == TRUE) { road = 1; } else { road = 0; } // add road links for(i=0;i<NUM_MAP_LOCATIONS;i++) { int distHere = getDist(ourMap, ROAD, from, i); if(distHere != NO_EDGE && distHere <= road) { canReach[i] = TRUE; } } if(sea == TRUE) { sea = 1; } else { sea = 0; } // add sea links for(i=0;i<NUM_MAP_LOCATIONS;i++) { int distHere = getDist(ourMap, BOAT, from, i); if(distHere != NO_EDGE && distHere <= sea) { canReach[i] = TRUE; } } // ensure Dracula can't move to the hospital if(player == PLAYER_DRACULA) { canReach[ST_JOSEPH_AND_ST_MARYS] = FALSE; } // output // work out numLocations; initialise it to be 0 (*numLocations) = 0; // count number of locations for(i=0;i<NUM_MAP_LOCATIONS;i++) { if(canReach[i] == TRUE) { (*numLocations)++; } } // our return array; big enough to conserve memory LocationID *ret = (LocationID *)(malloc((*numLocations) * sizeof(LocationID))); // current index we're up to int upto = 0; for(i=0;i<NUM_MAP_LOCATIONS;i++) { if(canReach[i] == TRUE) { ret[upto] = i; upto++; } } return ret; }
// Creates a new HunterView to summarise the current state of the game HunterView newHunterView(char *pastPlays, PlayerMessage messages[]) { assert(pastPlays != NULL); assert(messages != NULL); // malloc HunterView hunterView = malloc(sizeof(struct hunterView)); assert(hunterView != NULL); // setup the gameview hunterView->g = newGameView(pastPlays, messages); assert(hunterView->g != NULL); int i; // initialise Dracula's actual locations to all UNKNOWN_LOCATION for(i=0;i<TRAIL_SIZE;i++) { hunterView->trailLocs[i] = UNKNOWN_LOCATION; } // length of pastPlays int pastPlaysLength = strnlen(pastPlays, MAX_PAST_PLAYS_LENGTH); // iterate over each turn and process for(i=0;i<pastPlaysLength;i+=CHARS_PER_PLAY_BLOCK) { // get curPlayer PlayerID curPlayer = ( (i/CHARS_PER_PLAY_BLOCK) % NUM_PLAYERS); // ensure it's dracula if(curPlayer == PLAYER_DRACULA) { // try to get current loc (if it's exact) LocationID curLoc = abbrevToID(pastPlays+i+LOC_ABBREV_INDEX); // fprintf(stderr,"here = %.7s curLoc = %d\n",pastPlays+i,curLoc); // test if exact location if(!validPlace(curLoc)) { // not exact; try to use trail to work out where we really are // either a HIDE, DOUBLE_BACK_ or TELEPORT if(pastPlays[i+LOC_ABBREV_INDEX] == 'H') { // HIDE: go to most recent location curLoc = hunterView->trailLocs[LAST_TRAIL_LOC_INDEX]; } else if(pastPlays[i+LOC_ABBREV_INDEX] == 'D') { // DOUBLE BACK int numBack = (int)(pastPlays[i+LOC_ABBREV_INDEX+1]-'0'); curLoc = hunterView->trailLocs[numBack]; } else if(pastPlays[i+LOC_ABBREV_INDEX] == 'T') { // TELEPORT back to CASTLE_DRACULA curLoc = CASTLE_DRACULA; } else if(pastPlays[i+LOC_ABBREV_INDEX] == 'C') { curLoc = CITY_UNKNOWN; } else if(pastPlays[i+LOC_ABBREV_INDEX] == 'S') { curLoc = SEA_UNKNOWN; } else { curLoc = UNKNOWN_LOCATION; } } // push curLoc onto the trail pushOnTrailLocs(hunterView, curLoc); } } return hunterView; }
// What are the specified player's next possible moves LocationID *whereCanTheyGo(HunterView currentView, int *numLocations, PlayerID player, int road, int rail, int sea) { assert(currentView != NULL); assert(0 <= player && player < NUM_PLAYERS); assert(numLocations != NULL); Round theirNextRound; // check if they're before or after me if(player >= getCurrentPlayer(currentView->g)) { theirNextRound = getRound(currentView->g); } else { theirNextRound = getRound(currentView->g) + 1; } // return value LocationID *ret; // check if first round if(theirNextRound == FIRST_ROUND) { ret = (LocationID *)(malloc(sizeof(LocationID)*NUM_MAP_LOCATIONS)); (*numLocations) = 0; // everywhere! int i; for(i=0;i<NUM_MAP_LOCATIONS;i++) { // dracula can go everywhere except ST_JOSEPH_AND_ST_MARYS if(player != PLAYER_DRACULA || i != ST_JOSEPH_AND_ST_MARYS) { ret[(*numLocations)] = i; (*numLocations)++; } } } else { if(player == PLAYER_DRACULA) { // dracula // dracula's current location LocationID dracLoc = whereIs(currentView, PLAYER_DRACULA); // see if we can infer dracula's location // if valid, do the usual if(validPlace(dracLoc)) { // dracula can't travel by rail even if he wants to ret = connectedLocations(currentView->g, numLocations, whereIs(currentView, PLAYER_DRACULA), theirNextRound, player, road, FALSE, sea); } else { (*numLocations) = 0; // FIXME not sure what to return; probably doesn't matter ret = NULL; } } else { // a hunter ret = connectedLocations(currentView->g, numLocations, getLocation(currentView->g, player), theirNextRound, player, road, rail, sea); } } return ret; }
// given a Place number, return its type int idToType(LocationID p) { assert(validPlace(p)); return places[p].type; }
// Find out what minions are placed at the specified location void whatsThere(DracView currentView, LocationID where, int *numTraps, int *numVamps) { DracView dv; int count; LocationID trail[TRAIL_SIZE] = { -1 }; LocationID whereType; int index; int round; int trapSetRound; dv = currentView; count = 0; whereType = idToType(where); *numTraps = 0; *numVamps = 0; index = dv->pastPlaysIndex[PLAYER_DRACULA]; round = getLastRound(dv, PLAYER_DRACULA); trapSetRound = -1; // printf("whatsThere: round is: %d, currRound: %d\n", round, getRound(dv->gv)); /* * 1. Get minions placed by Drac * 1a. Get Drac's trail * 1b. if city is in the trail: increment for any traps/vamps * * 2. If traps found: * 2a. get each hunter's trail * 2b. check if city is in hunter's trail, AND if traps/vamps * have been encountered * 2c. if traps/vamps have been encountered, decrement them */ // 1. Get minions placed by Drac // 'where' restrictions: // - not in the sea, // - is a valid city number, // - not the hospital if (whereType != SEA && validPlace(where) && where != ST_JOSEPH_AND_ST_MARYS) { // 1a. getHistory(dv->gv, PLAYER_DRACULA, trail); // 1b. Store the traps set by Drac within that city, if applicable // if 'where' is within the trail while (count < TRAIL_SIZE) { // Starting at the latest round, check if the city the user // wants to check is in drac's trail. // If so, increment if any traps or vamps are there and note // which round in which they were set by Drac if (trail[count] == where) { // index+3 and index+4: the indices where // dracula's encounters will be stored if (dv->past[((round - count) * ROUND_LENGTH) + (index + MINION_ONE)] == 'T') { *numTraps += 1; //printf("yep0: loc[%c%c], round: %d, count: %d\n",dv->past[((round-count) * ROUND_LENGTH) + index+1], dv->past[((round-count) * ROUND_LENGTH) + index+2], round, count); } else if (dv->past[((round - count) * ROUND_LENGTH) + (index + MINION_ONE)] == 'V') { *numVamps += 1; } if (dv->past[((round - count) * ROUND_LENGTH) + (index + MINION_TWO)] == 'T') { *numTraps += 1; } else if (dv->past[((round - count) * ROUND_LENGTH) + (index + MINION_TWO)] == 'V') { *numVamps += 1; } // break out of the loop since 'where' can only be // in the trail once trapSetRound = round - count; count = TRAIL_SIZE; } count += 1; } } //printf("found drac's: at %s, traps: %d, vamps: %d\n", idToName(where), *numTraps, *numVamps); // 2. If traps found // Traps have been found at the given city. // Check to see if any hunters have visited that city since, // hence encountering and disabling that trap/vamp if (trapSetRound != -1 && *numTraps > 0 && *numVamps > 0) { int hunter; int index; hunter = 0; // go through all hunter's (NUM_PLAYERS-1) trails** // if a hunter has visited the 'where' city, and has // encountered a trap/vamp, then decrement trap/vamp // // **since drac's and hunter's giveMeTheTrails are out of sync, // go through the pastPlays string // Will need to do that anyway because we need to check the string // for T&V encounters while (hunter < NUM_PLAYERS-1) { index = dv->pastPlaysIndex[hunter]; round = getLastRound(dv, hunter); for (count = 0; count < TRAIL_SIZE; count++) { if (getRoundLocation(dv, round, hunter) == where) { if (dv->past[(round * ROUND_LENGTH) + (index + MINION_ONE)] == 'T') { *numTraps -= 1; } else if (dv->past[(round * ROUND_LENGTH) + (index + MINION_ONE)] == 'V') { *numTraps -= 1; } if (dv->past[(round * ROUND_LENGTH) + (index + MINION_TWO)] == 'T') { *numTraps -= 1; } else if (dv->past[(round * ROUND_LENGTH) + (index + MINION_TWO)] == 'V') { *numTraps -= 1; } } round -= 1; } hunter += 1; } } // printf("where: %s (%d), numTraps: %d, numVamps: %d\n", idToName(where), where, *numTraps, *numVamps); }
// given a Place number, return its name char *idToName(LocationID p) { assert(validPlace(p)); return places[p].name; }