Added logic to generate valid board, refactored generation logic and cleaned up overall structure

This commit is contained in:
Todd Kaiser
2021-03-03 20:33:26 -07:00
parent 0a4e66a6e7
commit e58d7d6d2a

View File

@@ -24,7 +24,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 225,
"execution_count": 715,
"metadata": {},
"outputs": [],
"source": [
@@ -33,15 +33,45 @@
},
{
"cell_type": "code",
"execution_count": 226,
"execution_count": 716,
"metadata": {},
"outputs": [],
"source": [
"random.seed()\n",
"\n",
"BOARD_WIDTH = 10\n",
"BOARD_HEIGHT = 10\n",
"\n",
"SHIPS = [ (\"BATTLESHIP\", 5),\n",
" (\"CRUISER\", 3),\n",
" (\"DESTROYER<A>\", 2),\n",
" (\"DESTROYER<B>\", 2) ]\n",
" \n",
"VALID_MOVES = [[-1, 0],\n",
" [-1, 1],\n",
" [ 0, 1],\n",
" [ 1, 1],\n",
" [ 1, 0],\n",
" [ 1,-1],\n",
" [ 0,-1],\n",
" [-1,-1]]"
]
},
{
"cell_type": "code",
"execution_count": 717,
"metadata": {},
"outputs": [],
"source": [
"# random number functions\n",
"#\n",
"# seed the random number generator\n",
"random.seed()\n",
"\n",
"# random_x_y\n",
"#\n",
"# generate a valid x,y coordinate on the board\n",
"# returns: x,y\n",
"# x: integer between 1 and BOARD_HEIGHT\n",
"# y: integer between 1 and BOARD WIDTH\n",
"def random_x_y():\n",
" x = random.randrange(1,BOARD_WIDTH+1)\n",
" y = random.randrange(1,BOARD_HEIGHT+1)\n",
@@ -50,14 +80,14 @@
},
{
"cell_type": "code",
"execution_count": 227,
"execution_count": 718,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"7 2\n"
"8 4\n"
]
}
],
@@ -68,183 +98,109 @@
},
{
"cell_type": "code",
"execution_count": 228,
"execution_count": 719,
"metadata": {},
"outputs": [],
"source": [
"def random_direction(valid_directions):\n",
" idx = random.randrange(len(valid_directions))\n",
" return valid_directions[idx]"
]
},
{
"cell_type": "code",
"execution_count": 229,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"0\n"
]
}
],
"source": [
"print(random_direction([0,1,2]))"
]
},
{
"cell_type": "code",
"execution_count": 230,
"metadata": {},
"outputs": [],
"source": [
"SHIPS = [ (\"BATTLESHIP\", 5),\n",
" (\"CRUISER\", 3),\n",
" (\"DESTROYER<A>\", 2),\n",
" (\"DESTROYER<B>\", 2) ]\n",
"# TODO: add an optional starting coordinate for testing\n",
"# purposes\n",
"def generate_ship_coordinates(ship):\n",
" \n",
" # randomly generate starting x,y coordinates\n",
" start_x, start_y = random_x_y()\n",
"\n",
"# given a coordinate (x,y) and a ship type,\n",
"# determine which directions from the coordinate \n",
"# a ship could be placed. Directions are numbered\n",
"# starting at 0 for up, 1 for up/right, 2 for right, \n",
"# etc., clockwise from zero (12 o'clock position)\n",
"# returns a vector of direction numbers where\n",
"# a ship can be placed, starting at one end of the\n",
"# ship to its length\n",
"def get_possible_directions(x,y,ship):\n",
" # using starting coordinates and the ship type,\n",
" # generate a vector of possible directions the ship \n",
" # could be placed. directions are numbered 0-7 along\n",
" # points of the compass (N, NE, E, SE, S, SW, W, NW)\n",
" # clockwise. a vector of valid directions where the\n",
" # ship does not go off the board is determined\n",
" ship_len = SHIPS[ship][1] - 1\n",
" dirs = [False for x in range(8)]\n",
" dirs[0] = (x - ship_len) >=1\n",
" dirs[2] = (y + ship_len) <= BOARD_WIDTH\n",
" dirs[0] = (start_x - ship_len) >=1\n",
" dirs[2] = (start_y + ship_len) <= BOARD_WIDTH\n",
" dirs[1] = dirs[0] and dirs[2]\n",
" dirs[4] = (x + ship_len) <= BOARD_HEIGHT\n",
" dirs[4] = (start_x + ship_len) <= BOARD_HEIGHT\n",
" dirs[3] = dirs[2] and dirs[4]\n",
" dirs[6] = (y - ship_len) >= 1\n",
" dirs[6] = (start_y - ship_len) >= 1\n",
" dirs[5] = dirs[4] and dirs[6]\n",
" dirs[7] = dirs[6] and dirs[0]\n",
" directions = [p for p in range(len(dirs)) if dirs[p]]\n",
"\n",
" return [x for x in range(len(dirs)) if dirs[x]]"
]
},
{
"cell_type": "code",
"execution_count": 231,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Boundary Conditions\n( 5, 5): [0, 1, 2, 3, 4, 5, 6, 7]\n( 1, 1): [2, 3, 4]\n( 1,10): [4, 5, 6]\n(10,10): [0, 6, 7]\n(10, 1): [0, 1, 2]\n"
]
}
],
"source": [
"print(\"Boundary Conditions\")\n",
"print(\"( 5, 5):\",get_possible_directions(5,5,0))\n",
"print(\"( 1, 1):\",get_possible_directions(1,1,0))\n",
"print(\"( 1,10):\",get_possible_directions(1,10,0))\n",
"print(\"(10,10):\",get_possible_directions(10,10,0))\n",
"print(\"(10, 1):\",get_possible_directions(10,1,0))"
]
},
{
"cell_type": "code",
"execution_count": 232,
"metadata": {},
"outputs": [],
"source": [
"VALID_MOVES = [[-1, 0],\n",
" [-1, 1],\n",
" [ 0, 1],\n",
" [ 1, 1],\n",
" [ 1, 0],\n",
" [ 1,-1],\n",
" [ 0,-1],\n",
" [-1,-1]]\n",
" # using the vector of valid directions, pick a\n",
" # random direction to place the ship\n",
" dir_idx = random.randrange(len(directions))\n",
" direction = directions[dir_idx]\n",
"\n",
"\n",
"def generate_ship_coordinates(x_start,y_start,direction,ship):\n",
" # using the starting x,y, direction and ship\n",
" # type, return the coordinates of each point \n",
" # of the ship. VALID_MOVES is a staic array\n",
" # of coordinate offsets to walk from starting\n",
" # coordinate to the end coordinate in the \n",
" # chosen direction\n",
" ship_len = SHIPS[ship][1] - 1\n",
" d_x = VALID_MOVES[direction][0]\n",
" d_y = VALID_MOVES[direction][1]\n",
"\n",
" coords = [(x_start,y_start)]\n",
" x_coord = x_start\n",
" y_coord = y_start\n",
" coords = [(start_x,start_y)]\n",
" x_coord = start_x\n",
" y_coord = start_y\n",
" for i in range(ship_len):\n",
" x_coord = x_coord + d_x\n",
" y_coord = y_coord + d_y\n",
" coords.append((x_coord,y_coord))\n",
" return coords"
" return coords\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 233,
"execution_count": 720,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"('BATTLESHIP', 5) 2 2 4 [(2, 2), (3, 2), (4, 2), (5, 2), (6, 2)]\n",
"('CRUISER', 3) 6 7 3 [(6, 7), (7, 8), (8, 9)]\n",
"('DESTROYER<A>', 2) 1 1 3 [(1, 1), (2, 2)]\n",
"('DESTROYER<B>', 2) 8 3 6 [(8, 3), (8, 2)]\n"
"BATTLESHIP 5 [(9, 2), (8, 3), (7, 4), (6, 5), (5, 6)]\nCRUISER 3 [(3, 8), (2, 9), (1, 10)]\nDESTROYER<A> 2 [(3, 5), (3, 6)]\nDESTROYER<B> 2 [(6, 5), (7, 4)]\n"
]
}
],
"source": [
"\n",
"for ship in range(len(SHIPS)):\n",
" x,y = random_x_y()\n",
" directions = get_possible_directions(x,y,ship)\n",
" direction = random_direction(directions)\n",
" coords = generate_ship_coordinates(x,y,direction,ship)\n",
" print(SHIPS[ship],x,y,direction,coords)"
" coords = generate_ship_coordinates(ship)\n",
" print(f'{SHIPS[ship][0]:15}',f'{SHIPS[ship][1]:2}',coords)"
]
},
{
"cell_type": "code",
"execution_count": 234,
"execution_count": 721,
"metadata": {},
"outputs": [],
"source": [
"computer_board = [ [ -1 for y in range(BOARD_WIDTH)] \n",
" for x in range(BOARD_HEIGHT)]\n",
"player_board = [ [ -1 for y in range(BOARD_WIDTH)] \n",
" for x in range(BOARD_HEIGHT)]"
"def create_blank_board():\n",
" return [ [ None for y in range(BOARD_WIDTH)] \n",
" for x in range(BOARD_HEIGHT)]"
]
},
{
"cell_type": "code",
"execution_count": 235,
"execution_count": 722,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
" 1 2 3 4 5 6 7 8 9 10\n",
" 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 3 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 4 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 5 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 6 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 7 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 8 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 9 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
"10 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n"
" 1 2 3 4 5 6 7 8 9 10\n 1 \n 2 \n 3 \n 4 \n 5 \n 6 \n 7 \n 8 \n 9 \n10 \n"
]
}
],
"source": [
"def print_board(board):\n",
"\n",
" # print board header (column numbers)\n",
" print(' ',end='')\n",
" for z in range(BOARD_WIDTH):\n",
" print(f'{z+1:3}',end='')\n",
@@ -253,62 +209,122 @@
" for x in range(len(board)):\n",
" print(f'{x+1:2}',end='')\n",
" for y in range(len(board[x])):\n",
" print(f\"{board[x][y]:3}\",end='')\n",
" if(board[x][y] is None):\n",
" print(f\"{' ':3}\",end='')\n",
" else:\n",
" print(f\"{board[x][y]:3}\",end='')\n",
" print('')\n",
"\n",
"computer_board = create_blank_board()\n",
"print_board(computer_board)"
]
},
{
"cell_type": "code",
"execution_count": 236,
"execution_count": 723,
"metadata": {},
"outputs": [],
"source": [
"def place_ship(board,coords,ship):\n",
" for coord in coords:\n",
" print(\"--->\",coord)\n",
" board[coord[0]-1][coord[1]-1] = 0"
" board[coord[0]-1][coord[1]-1] = ship"
]
},
{
"cell_type": "code",
"execution_count": 237,
"execution_count": 724,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[(7, 5), (6, 4), (5, 3), (4, 2), (3, 1)]\n",
"---> (7, 5)\n",
"---> (6, 4)\n",
"---> (5, 3)\n",
"---> (4, 2)\n",
"---> (3, 1)\n",
"DESTROYER<B> 2 [(1, 7), (2, 6), (3, 5), (4, 4), (5, 3)]\n",
" 1 2 3 4 5 6 7 8 9 10\n",
" 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 3 0 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 4 -1 0 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 5 -1 -1 0 -1 -1 -1 -1 -1 -1 -1\n",
" 6 -1 -1 -1 0 -1 -1 -1 -1 -1 -1\n",
" 7 -1 -1 -1 -1 0 -1 -1 -1 -1 -1\n",
" 8 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
" 9 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n",
"10 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n"
" 1 0 \n",
" 2 0 \n",
" 3 0 \n",
" 4 0 \n",
" 5 0 \n",
" 6 \n",
" 7 \n",
" 8 \n",
" 9 \n",
"10 \n"
]
}
],
"source": [
"x,y = random_x_y()\n",
"ship = 0\n",
"directions = get_possible_directions(x,y,ship)\n",
"direction = random_direction(directions)\n",
"coords = generate_ship_coordinates(x,y,direction,ship)\n",
"print(coords)\n",
"place_ship(computer_board,coords,0)\n",
"print_board(computer_board)"
"# test place_ship\n",
"board = create_blank_board()\n",
"coords = generate_ship_coordinates(0)\n",
"print(f'{SHIPS[ship][0]:15}',f'{SHIPS[ship][1]:2}',coords)\n",
"place_ship(board,coords,0)\n",
"print_board(board)"
]
},
{
"cell_type": "code",
"execution_count": 725,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
" 1 2 3 4 5 6 7 8 9 10\n 1 1 0 \n 2 1 0 \n 3 1 0 \n 4 0 \n 5 2 0 \n 6 2 \n 7 3 3 \n 8 \n 9 \n10 \n"
]
}
],
"source": [
"# NOTE: A little quirk that exists here and in the orginal\n",
"# game: Ships are allowed to cross each other!\n",
"# For example: 2 destroyers, length 2, one at\n",
"# [(1,1),(2,2)] and other at [(2,1),(1,2)]\n",
"\n",
"def generate_board():\n",
" board = create_blank_board()\n",
"\n",
" for ship in range(len(SHIPS)):\n",
" placed = False\n",
" coords = []\n",
" while not placed:\n",
" coords = generate_ship_coordinates(ship)\n",
" clear = True\n",
" for coord in coords:\n",
" if board[coord[0]-1][coord[1]-1] is not None:\n",
" clear = False\n",
" break\n",
" if clear:\n",
" placed = True\n",
" place_ship(board,coords,ship)\n",
" return board\n",
"\n",
"print_board(generate_board())"
]
},
{
"cell_type": "code",
"execution_count": 726,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[(4, 4), (2, 8), (10, 6), (2, 4), (10, 7)]\n"
]
}
],
"source": [
"def generate_shots(number):\n",
" shots = []\n",
" for i in range(number):\n",
" shots.append(random_x_y())\n",
" return shots\n",
"\n",
"shots = generate_shots(5)\n",
"print(shots)"
]
},
{