Sorry, I've been at school all week, so I've been unable to update with this program. But maybe absense has mad all yawl's hears go fonder. So you should be very excited to finally have...
Battleship
This is the biggest program that I have to contribute. However, it is a game that should need little introduction. Battleship. Place your ships, hunt theirs, hope you win. The computer plays against you on this one. The routine for choosing where to shoot is not the best out there, but it's not entirely stupid either.
Here's what the game looks like in play:
Code:
Your shot coordinates : f9
Miss.
My shot : g4
HIT!
I sunk your cruser.
****** R A D A R********************** F L E E T
_ 1 2 3 4 5 6 7 8 9 10**********_ 1 2 3 4 5 6 7 8 9 10
a . . . . . . . . . .********** a . . . . . o . . . .
b . . . . . . . . . .********** b . . . . . . . . . .
c o . . . . . . . . .********** c . . 2 2 2 . . . . .
d X . o . . . . . . .********** d . . . . . . . . . 5
e X . . . . . . . . .********** e . . . . 1 1 . . . 5
f X . . . o . . . o .********** f . o o . 4 4 4 4 . 5
g X . . . . o . . . .********** g o X X X . . . . . 5
h . . . . . . o . . .********** h . o . . . . . . . 5
i . . . . . . . . . .********** i . . . . . . . o . o
j . . . . . . . . o .********** j . . . . o . . . . .
****carrier : #####**************** carrier : #####
battleship : SUNK************** battleship : ####
**** cruser : ###********************cruser : ...
**submarine : ###**************** submarine : ###
****pt ship : ##********************pt ship : ##
Your shot coordinates :c5
You may notice that the game is set up side-by-side instead of up and down like a battleship game in real life is. This is purely because up and down didn't fit, but side to side fits with enough room for you to see the results of the previous move, which is a good thing because that's where it says when you sink something.
So why don't we just get to the code:
Code:
/* Batttleship (R)
* written by Joseph Larson 2005
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#define PL 0
#define CP 1
#define FULL(x) ((x + (x < 2)) + 1)
#define ADD(a,b) if (!bd[a][b].chit) {t[i].x = a; t[i].y = b; i++;}
typedef struct {
**unsigned int x : 4;
**unsigned int y : 4;
} COORD;
typedef struct {
**unsigned int pship : 3;
**unsigned int chit : 1;
**unsigned int cship : 3;
**unsigned int phit :3;
} GRID;
GRID bd[10][10];
COORD t[51];
char ship_life[2][5];
char *ship_name[5] = {"pt ship", "submarine", "cruser", "battleship", "carrier"};
COORD getloc (void) {
**char input[10];
**COORD loc;
**loc.x = loc.y = input[0] = 0;
**do {
****if (input[0]) printf ("Invalad location, letter first then number : ");
****scanf ("%s", input);
****if (isalpha (input[0]) && (loc.x = atoi (&input[1]))) {
******loc.y = tolower (input[0]) - 'a';
******if (loc.y > 9 || loc.x > 10 || loc.x < 0) loc.x = 0;
****}
**} while (!loc.x);
**loc.x --;
**return loc;
}
void show_board (void) {
**int x, y;
**
**printf ("%16s\t\t%16s\n_ 1 2 3 4 5 6 7 8 9 10\t\t_ 1 2 3 4 5 6 7 8 9 10"
****,"R A D A R","F L E E T");
**for (y = 0; y < 10; y++) {
****printf ("\n%c ", y + 'a');
****for (x = 0; x < 10; x++)
******printf ("%c ", (bd[x][y].phit) ? (bd[x][y].cship) ? 'X' : 'o' : '.');
****printf ("\t\t%c ", y + 'a');
****for (x = 0; x < 10; x++)
******printf ("%c ", (bd[x][y].chit) ? (bd[x][y].pship) ? 'X'
******: 'o' : ".12345"[bd[x][y].pship]);
**}
**for (y = 4; y >= 0; y--) {
****printf ("\n %10s : ", ship_name[y]);
****if (ship_life[CP][y]) for(x = 0; x < FULL(y); x++) putchar ('#');
****else printf ("SUNK");
****printf ("\t\t %10s : ", ship_name[y]);
****for (x = 0; x < FULL(y); x++) putchar (".#"[ship_life[PL][y] > x]);
**}
}
int valad_ship (COORD s, COORD e, int c) {
**int v, check, d;
**COORD step;
**
**check = abs ((s.x + 10 * s.y) - (e.x + 10 * e.y));
**if (check % (FULL(c) - 1)) {
****printf ("\nInvalad location. The %s is only %d long\n"
************"and ships can only be placed vertical or horizontal.\n",
************ship_name[c], FULL(c));
****v = 0;
**} else {
****step.x = step.y = 0;
****if ((check / (FULL(c) - 1)) - 1) step.y = 1;
****else step.x = 1;
****if (s.x > e.x) s.x = e.x;
****if (s.y > e.y) s.y = e.y;
****for (d = 0; d < FULL(c) && v; d++) {
******check = bd[s.x + d * step.x][s.y + d * step.y].pship;
******if (check && check != 7) {
********printf ("\nInvalad location. Ships can not overlap.\n");
********v = 0;
******}
****}
**}
**if (v) for (d = 0; d < FULL(c); d++)
****bd[s.x + d * step.x][s.y + d * step.y].pship = c + 1;
**return v;
}
void player_setup (void) {
**int ship, d;
**COORD start, end;
**
**for (ship = 4; ship >= 0; ship--)
****do {
******show_board ();
******printf ("\nEnter start location for your %s : ", ship_name[ship]);
******start = getloc();
******printf ("Enter end location (length %d) : ", FULL(ship));
******end = getloc();
****} while (!valad_ship (start, end, ship));
**show_board ();
}
void auto_setup (int pl) {
**COORD s, step;
**int c, d;
**
**for (c = 0; c < 5; c++) {
****do {
******s.x = rand() % 10; s.y = rand() % 10;
******step.x = step.y = 0;
******if (rand() < RAND_MAX / 2) {
********step.x = 1;
********if (s.x + FULL(c) > 10) s.x -= FULL(c);
******} else {
********step.y = 1;
********if (s.y + FULL(c) > 10) s.y -= FULL(c);
******}
******for (d = 0; d < FULL(c) &&
******(pl) ? !bd[s.x + d * step.x][s.y + d * step.y].cship
******: !bd[s.x + d * step.x][s.y + d * step.y].pship; d++);
****} while (d < FULL(c));
****for (d = 0; d < FULL(c); d++)
******(pl) ? bd[s.x + d * step.x][s.y + d * step.y].cship
******: bd[s.x + d * step.x][s.y + d * step.y].pship = c + 1;
**}
}
void init (void) {
**int c, d;
**char input;
**
**srand (time (NULL));
**for (c = 0; c < 10; c++)
****for (d = 0; d < 10; d++)
******bd[c][d].pship = bd[c][d].chit = bd[c][d].cship = bd[c][d].phit = 0;
**for (c = 0; c < 5; c++)
****ship_life[PL][c] = ship_life[CP][c] = FULL(c);
**printf ("Battleship (R)\n\nDo you want (A)uto or (M)anual setup ? (a/m) ");
**while (!isalpha (input = getchar()));
**if (tolower (input) == 'm')
****player_setup ();
**else auto_setup (PL);
**auto_setup (CP);
}
int check_for_lose (int player) {
**int c;
**
**for (c = 0; c < 5 && !ship_life[player][c]; c++);
**return (c == 5);
}
void player_turn (void) {
**COORD shot;
**int ship;
**
**show_board ();
**printf ("\n\nYour shot coordinates : ");
**shot = getloc ();
**if (bd[shot.x][shot.y].phit)
****printf ("A wasted shot! You already fired there!\n");
**else {
****bd[shot.x][shot.y].phit = 1;
****if (ship = bd[shot.x][shot.y].cship) {
******printf ("HIT!\n");
******if (!(--ship_life[CP][--ship]))
********printf ("You sunk my %s.\n",ship_name[ship]);
****} else printf ("Miss.\n");
**}
}
int hit_no_sink (int x, int y) {
**if (bd[x][y].chit)
****if (bd[x][y].pship == 7)
******return 1;
****else if (bd[x][y].pship)
******if (ship_life[PL][bd[x][y].pship - 1])
********return 1;
**return 0;
}
int fill_t (void) {
**COORD c, d;
**int m[5] = {0, 1, 0, -1, 0};
**int x, i = 0;
**
**for (c.x = 0; c.x < 10; c.x++)
****for (c.y = 0; c.y < 10; c.y++)
******if (hit_no_sink (c.x,c.y)) {
********for (x = 0; x < 4; x++)
**********if (c.x + m[x] >= 0 && c.x + m[x] < 10
************&& c.y + m[x + 1] >= 0 && c.y + m[x + 1] < 10) {
************if (hit_no_sink (c.x + m[x], c.y + m[x + 1])) {
**************d.x = c.x; d.y = c.y;
**************while (d.x >= 0 && d.x < 10 && d.y >= 0 && d.y < 10
****************&& hit_no_sink (d.x, d.y)) {d.x -= m[x]; d.y -= m[x + 1];}
**************if (d.x >= 0 && d.x < 10 && d.y >= 0 && d.y < 10) ADD (d.x, d.y);
************}
**********}
********if (!i)
**********for (x = 0; x < 4; x++)
************if (c.x + m[x] >= 0 && c.x + m[x] < 10
**************&& c.y + m[x + 1] >= 0 && c.y + m[x + 1] < 10)
**************ADD (c.x + m[x], c.y + m[x + 1]);
******}
**if (!i)
****for (c.x = 0; c.x < 10; c.x++)
******for (c.y = 0; c.y < 10; c.y++)
********if ((c.x + c.y) % 2) ADD (c.x, c.y);
**return i;
}
void compy_turn (void) {
**int z, c, d, v;
**char input;
**COORD s, e;
**
**c = fill_t ();
**z = rand () % c;
**printf ("\nMy shot : %c%d\n", t[z].y + 'a', t[z].x + 1);
**bd[t[z].x][t[z].y].chit = 1;
**if (c = bd[t[z].x][t[z].y].pship) {
****printf ("HIT!\n");
****if (!(--ship_life[PL][c - 1]))
******printf ("I sunk your %s.\n", ship_name[c - 1]);
**} else printf ("Miss.\n");
}
void play (void) {
**int winner = 0;
**
**if (rand () < RAND_MAX / 2) {
****printf ("\nYou go first.\n");
****player_turn ();
**} else printf ("\nI'll go first.\n");
**do {
****compy_turn ();
****if (check_for_lose (PL)) {
******winner = 1;
******printf ("\nI win!\n");
****} else {
******player_turn ();
******if (check_for_lose (CP)) {
********winner = 1;
********printf ("\nYou win!\n");
******}
****}
**} while (!winner);
**show_board ();
}
int play_again (void) {
**char input;
**printf ("\nDo you wish to play again? (y/n) ");
**while (!isalpha (input = getchar()));
**if (tolower (input) != 'n') return 1;
**else return 0;
}
int main (void) {
**init ();
**do {play ();} while (play_again ());
**printf ("\nIt's been fun! So long Admiral!\n");
**exit (0);
}
Techinal note; the way the computer chooses it's shot is mostly handled by the function fill_t, or "fill target list." The comptuer analizes the board and makes a list of the best shots. If it's made an isolated hit, it fills the list with the spaces up, down, left and right of it, provided they're not already misses. If it's made two hits in a row it fills the list with the shots at the end, provided they're not already misses. If the list is still 0 in length, or it hasn't found any potential targets up to now, then the list is populated with ever other shot, resulting in a checkerboard pattern of possibilities. (No point in missing 3 in a row if the smallest ship is 2 in length.) Then it just chooses randomly from this list. Check it out and see if you can see this in action.
Now, if a clever programmer out there want to try to make a smaller version of this, go say for it. I'm actually disapointed with how humongus this is. This, Awari, and Cell I wish I could reduce a bit.
Now is when I beg you for you support. How do you support this great project? Just choose a program from the following list and tell me you want to play it. Because as long as this has got your support we're not stopping until the list is empty:
- Reverse (order a list of number by turning them around)
- Black Box (find molecules in the inky depths)
- Acey Deucy (a card game of highs, lows, and middles)
- Flash Cards (with pretty output, practice your math)
- Pickup Piles (1000 games in one, set the rules and play)
- Hangman (guess the word before you dangle)
- Rotate (like those sliding block puzzles but that you rotate pieces)