package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) const ( SEA_WIDTH = 6 DESTROYER_LENGTH = 2 CRUISER_LENGTH = 3 CARRIER_LENGTH = 4 ) type Point [2]int type Vector Point type Sea [][]int func NewSea() Sea { s := make(Sea, 6) for r := 0; r < SEA_WIDTH; r++ { c := make([]int, 6) s[r] = c } return s } func getRandomVector() Vector { v := Vector{} for { v[0] = rand.Intn(3) - 1 v[1] = rand.Intn(3) - 1 if !(v[0] == 0 && v[1] == 0) { break } } return v } func addVector(p Point, v Vector) Point { newPoint := Point{} newPoint[0] = p[0] + v[0] newPoint[1] = p[1] + v[1] return newPoint } func isWithinSea(p Point, s Sea) bool { return (1 <= p[0] && p[0] <= len(s)) && (1 <= p[1] && p[1] <= len(s)) } func valueAt(p Point, s Sea) int { return s[p[1]-1][p[0]-1] } func reportInputError() { fmt.Printf("INVALID. SPECIFY TWO NUMBERS FROM 1 TO %d, SEPARATED BY A COMMA.\n", SEA_WIDTH) } func getNextTarget(s Sea) Point { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("\n?") scanner.Scan() vals := strings.Split(scanner.Text(), ",") if len(vals) != 2 { reportInputError() continue } x, xErr := strconv.Atoi(strings.TrimSpace(vals[0])) y, yErr := strconv.Atoi(strings.TrimSpace(vals[1])) if (len(vals) != 2) || (xErr != nil) || (yErr != nil) { reportInputError() continue } p := Point{} p[0] = x p[1] = y if isWithinSea(p, s) { return p } } } func setValueAt(value int, p Point, s Sea) { s[p[1]-1][p[0]-1] = value } func hasShip(s Sea, code int) bool { hasShip := false for r := 0; r < SEA_WIDTH; r++ { for c := 0; c < SEA_WIDTH; c++ { if s[r][c] == code { hasShip = true break } } } return hasShip } func countSunk(s Sea, codes []int) int { sunk := 0 for _, c := range codes { if !hasShip(s, c) { sunk += 1 } } return sunk } func placeShip(s Sea, size, code int) { for { start := Point{} start[0] = rand.Intn(SEA_WIDTH) + 1 start[1] = rand.Intn(SEA_WIDTH) + 1 vector := getRandomVector() point := start points := []Point{} for i := 0; i < size; i++ { point = addVector(point, vector) points = append(points, point) } clearPosition := true for _, p := range points { if !isWithinSea(p, s) { clearPosition = false break } if valueAt(p, s) > 0 { clearPosition = false break } } if !clearPosition { continue } for _, p := range points { setValueAt(code, p, s) } break } } func setupShips(s Sea) { placeShip(s, DESTROYER_LENGTH, 1) placeShip(s, DESTROYER_LENGTH, 2) placeShip(s, CRUISER_LENGTH, 3) placeShip(s, CRUISER_LENGTH, 4) placeShip(s, CARRIER_LENGTH, 5) placeShip(s, CARRIER_LENGTH, 6) } func printIntro() { fmt.Println(" BATTLE") fmt.Println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println() fmt.Println("THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION") fmt.Println("HAS BEEN CAPTURED BUT NOT DECODED: ") fmt.Println() } func printInstructions() { fmt.Println() fmt.Println() fmt.Println("DE-CODE IT AND USE IT IF YOU CAN") fmt.Println("BUT KEEP THE DE-CODING METHOD A SECRET.") fmt.Println() fmt.Println("START GAME") } func printEncodedSea(s Sea) { for x := 0; x < SEA_WIDTH; x++ { fmt.Println() for y := SEA_WIDTH - 1; y > -1; y-- { fmt.Printf(" %d", s[y][x]) } } fmt.Println() } func wipeout(s Sea) bool { for c := 1; c <= 7; c++ { if hasShip(s, c) { return false } } return true } func main() { rand.Seed(time.Now().UnixNano()) s := NewSea() setupShips(s) printIntro() printEncodedSea(s) printInstructions() splashes := 0 hits := 0 for { target := getNextTarget(s) targetValue := valueAt(target, s) if targetValue < 0 { fmt.Printf("YOU ALREADY PUT A HOLE IN SHIP NUMBER %d AT THAT POINT.\n", targetValue) } if targetValue <= 0 { fmt.Println("SPLASH! TRY AGAIN.") splashes += 1 continue } fmt.Printf("A DIRECT HIT ON SHIP NUMBER %d\n", targetValue) hits += 1 setValueAt(targetValue*-1, target, s) if !hasShip(s, targetValue) { fmt.Println("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.") fmt.Println("SO FAR, THE BAD GUYS HAVE LOST") fmt.Printf("%d DESTROYER(S), %d CRUISER(S), AND %d AIRCRAFT CARRIER(S).\n", countSunk(s, []int{1, 2}), countSunk(s, []int{3, 4}), countSunk(s, []int{5, 6})) } if !wipeout(s) { fmt.Printf("YOUR CURRENT SPLASH/HIT RATIO IS %2f\n", float32(splashes)/float32(hits)) continue } fmt.Printf("YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET WITH A FINAL SPLASH/HIT RATIO OF %2f\n", float32(splashes)/float32(hits)) if splashes == 0 { fmt.Println("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.") } fmt.Println("\n****************************") break } }