From e58d7d6d2aef1a152f7674a7156f2c6425e5cac4 Mon Sep 17 00:00:00 2001 From: Todd Kaiser Date: Wed, 3 Mar 2021 20:33:26 -0700 Subject: [PATCH] Added logic to generate valid board, refactored generation logic and cleaned up overall structure --- 77 Salvo/python/salvo.ipynb | 322 +++++++++++++++++++----------------- 1 file changed, 169 insertions(+), 153 deletions(-) diff --git a/77 Salvo/python/salvo.ipynb b/77 Salvo/python/salvo.ipynb index d1681dfb..0c8287af 100644 --- a/77 Salvo/python/salvo.ipynb +++ b/77 Salvo/python/salvo.ipynb @@ -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\", 2),\n", + " (\"DESTROYER\", 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\", 2),\n", - " (\"DESTROYER\", 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', 2) 1 1 3 [(1, 1), (2, 2)]\n", - "('DESTROYER', 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 2 [(3, 5), (3, 6)]\nDESTROYER 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 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)" ] }, {