gitpush
37
demo/clients.js
Normal file
@ -0,0 +1,37 @@
|
||||
class Client {
|
||||
constructor(client) {
|
||||
this.client = client
|
||||
this.status = 'await'
|
||||
this.name = 'anonymous'
|
||||
this.client.emit('id', client.id)
|
||||
}
|
||||
}
|
||||
|
||||
class Clients {
|
||||
|
||||
constructor(io) {
|
||||
this.io = io
|
||||
this.clients = {}
|
||||
}
|
||||
|
||||
debuglog() {
|
||||
console.log('client total:', this.length)
|
||||
}
|
||||
|
||||
add(client) {
|
||||
this.clients[client.id] = new Client(client)
|
||||
this.debuglog()
|
||||
}
|
||||
|
||||
del(client) {
|
||||
delete this.clients[client.id]
|
||||
this.debuglog()
|
||||
}
|
||||
|
||||
get length() {
|
||||
return Object.entries(this.clients).length
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Clients
|
BIN
demo/dbclickrotate/favicon.ico
Normal file
After Width: | Height: | Size: 164 KiB |
22
demo/dbclickrotate/index.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>rotate</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="ship carrier">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<script src="main.js" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
2
demo/dbclickrotate/main.js
Normal file
@ -0,0 +1,2 @@
|
||||
const ship = document.querySelector('.ship')
|
||||
ship.addEventListener('dblclick', e => e.target.parentNode.classList.toggle("vertical"))
|
38
demo/dbclickrotate/style.css
Normal file
@ -0,0 +1,38 @@
|
||||
body {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ship {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.ship.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ship span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.ship .head {
|
||||
border-radius: 20px 0 0 20px;
|
||||
}
|
||||
|
||||
.ship .tail {
|
||||
border-radius: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.ship.vertical .head {
|
||||
border-radius: 20px 20px 0 0;
|
||||
}
|
||||
|
||||
.ship.vertical .tail {
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
BIN
demo/demo1/favicon.ico
Normal file
After Width: | Height: | Size: 164 KiB |
46
demo/demo1/index.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Battleship</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="index.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Battleship</h1>
|
||||
<input type="text" id="playerNameInput" placeholder="Enter your name">
|
||||
<button onclick="start()">start</button>
|
||||
<div id="playerGrid" class="grid"></div>
|
||||
<div class="ship carrier" draggable="true">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="ship battleship" draggable="true">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="ship destroyer" draggable="true">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="ship submarine" draggable="true">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="ship patrolboat" draggable="true">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
37
demo/demo1/index.js
Normal file
@ -0,0 +1,37 @@
|
||||
// elements in dom
|
||||
const playerNameInput = document.getElementById('playerNameInput')
|
||||
const playerGrid = document.getElementById('playerGrid')
|
||||
|
||||
const player = {
|
||||
name: localStorage.getItem('playerName') || '',
|
||||
}
|
||||
|
||||
// init
|
||||
playerNameInput.value = player.name
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const cell = document.createElement('span')
|
||||
cell.classList.add('cell')
|
||||
playerGrid.appendChild(cell)
|
||||
}
|
||||
|
||||
function start() {
|
||||
const playerName = playerNameInput.value.trim()
|
||||
if (playerName === '') {
|
||||
alert('Please enter your name!')
|
||||
playerNameInput.value = player.name || ''
|
||||
playerNameInput.focus()
|
||||
} else {
|
||||
player.name = playerName
|
||||
localStorage.setItem('playerName', playerName)
|
||||
console.info(`player ${playerName} is starting a game`)
|
||||
}
|
||||
}
|
||||
|
||||
for (let ship of document.querySelectorAll('.ship')) {
|
||||
ship.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
console.log(e)
|
||||
e.target.classList.toggle("v")
|
||||
})
|
||||
}
|
||||
|
60
demo/demo1/style.css
Normal file
@ -0,0 +1,60 @@
|
||||
* {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 430px;
|
||||
height: 430px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
background-color: darkslategray;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.grid span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 1px;
|
||||
background-color: darkcyan;
|
||||
}
|
||||
|
||||
.ship {
|
||||
width: 210px;
|
||||
height: 42px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ship.v {
|
||||
width: 42px;
|
||||
height: 210px;
|
||||
}
|
||||
|
||||
.ship span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
.carrier span {
|
||||
background-color: wheat;
|
||||
}
|
||||
|
||||
.battleship span {
|
||||
background-color: darkslateblue;
|
||||
}
|
||||
|
||||
.destroyer span {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.submarine span {
|
||||
background-color: darkslategray;
|
||||
}
|
||||
|
||||
.patrolboat span {
|
||||
background-color: darkgoldenrod;
|
||||
}
|
BIN
demo/dragdrop/favicon.ico
Normal file
After Width: | Height: | Size: 164 KiB |
19
demo/dragdrop/index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>dragdrop</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="one">
|
||||
<span id="box" draggable="true"></span>
|
||||
</div>
|
||||
<div id="two"></div>
|
||||
<script src="main.js" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
16
demo/dragdrop/main.js
Normal file
@ -0,0 +1,16 @@
|
||||
const one = document.getElementById('one')
|
||||
const tow = document.getElementById('tow')
|
||||
const box = document.getElementById('box')
|
||||
|
||||
let item = undefined
|
||||
box.addEventListener('drag', e => item = e.target)
|
||||
|
||||
one.addEventListener('dragover', e => e.preventDefault())
|
||||
two.addEventListener('dragover', e => e.preventDefault())
|
||||
one.addEventListener('drop', drop)
|
||||
two.addEventListener('drop', drop)
|
||||
|
||||
function drop(e) {
|
||||
e.target.appendChild(item)
|
||||
item = undefined
|
||||
}
|
29
demo/dragdrop/style.css
Normal file
@ -0,0 +1,29 @@
|
||||
body {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#one,
|
||||
#two {
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
#box {
|
||||
background-color: chartreuse;
|
||||
}
|
BIN
demo/onshipgrid/favicon.ico
Normal file
After Width: | Height: | Size: 164 KiB |
44
demo/onshipgrid/index.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>grid</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="grid"></div>
|
||||
<div class="ship carrier" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship battleship" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship cruiser" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship submarine" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship destroyer" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<button id='testButton'>planning</button>
|
||||
<script src="main.js" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
130
demo/onshipgrid/main.js
Normal file
@ -0,0 +1,130 @@
|
||||
const grid = document.querySelector('.grid')
|
||||
const ships = document.querySelectorAll('.ship')
|
||||
const carrier = document.querySelector('.carrier') // 航空母舰
|
||||
const battleship = document.querySelector('.battleship') // 战列舰
|
||||
const cruiser = document.querySelector('.cruiser') // 巡洋舰
|
||||
const submarine = document.querySelector('.submarine') // 潜艇
|
||||
const destroyer = document.querySelector('.destroyer') // 驱逐舰
|
||||
const testButton = document.querySelector('#testButton')
|
||||
|
||||
let playing = false
|
||||
let dragingItem = null
|
||||
|
||||
function mod(n, w) {
|
||||
const x = n % w
|
||||
const y = (n - x) / w
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
function checkIfCollision(ox, oy, currentShip) {
|
||||
for (let ship of ships) {
|
||||
if (ship != currentShip) {
|
||||
const cell = ship.parentNode
|
||||
const index = parseInt(cell.id.slice(5))
|
||||
let { x, y } = mod(index, 10)
|
||||
const vertical = ship.classList.contains('vertical')
|
||||
const length = ship.children.length
|
||||
if (vertical) {
|
||||
const tail = y + length
|
||||
for (; y < tail; y++) {
|
||||
if (ox === x && oy === y) return true
|
||||
}
|
||||
} else {
|
||||
const tail = x + length
|
||||
for (; x < tail; x++) {
|
||||
if (ox === x && oy === y) return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function shipRotate(e) {
|
||||
if (playing) return
|
||||
const currentShip = e.target.parentNode
|
||||
const length = currentShip.children.length
|
||||
const cell = currentShip.parentNode
|
||||
const index = parseInt(cell.id.slice(5))
|
||||
let { x, y } = mod(index, 10)
|
||||
const vertical = currentShip.classList.contains('vertical')
|
||||
// TODO 检查碰撞
|
||||
if (vertical) {
|
||||
// 判断转为横向时是否碰撞
|
||||
const tail = x + length - 1
|
||||
if (tail >= 10) return // 船尾越界
|
||||
for (x = x + 1; x <= tail; x++) {
|
||||
if (checkIfCollision(x, y, currentShip)) return
|
||||
}
|
||||
} else {
|
||||
// 判断转为竖向时是否碰撞
|
||||
const tail = y + length - 1
|
||||
if (tail >= 10) return // 船尾越界
|
||||
for (y = y + 1; y <= tail; y++) {
|
||||
if (checkIfCollision(x, y, currentShip)) return
|
||||
}
|
||||
}
|
||||
|
||||
currentShip.classList.toggle('vertical')
|
||||
}
|
||||
|
||||
function shipMove(ship, x, y) {
|
||||
const index = 10 * y + x
|
||||
const vertical = ship.classList.contains('vertical')
|
||||
// TODO 检查碰撞
|
||||
if (vertical) { } else { }
|
||||
const cell = document.getElementById(`cell-${index}`)
|
||||
cell.appendChild(ship)
|
||||
}
|
||||
|
||||
for (let ship of ships) ship.addEventListener('dblclick', shipRotate)
|
||||
|
||||
testButton.addEventListener('click', e => {
|
||||
playing = !playing
|
||||
e.target.innerHTML = playing ? 'playing' : 'planning'
|
||||
})
|
||||
|
||||
document.addEventListener('dragstart', e => {
|
||||
if (playing || e.target.classList && !e.target.classList.contains('ship')) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
dragingItem = e.target
|
||||
// document.body.append(dragingItem)
|
||||
})
|
||||
document.addEventListener('dragover', e => {
|
||||
e.preventDefault()
|
||||
dragingItem.style.display = 'none'
|
||||
// document.body.append(dragingItem);
|
||||
// dragingItem.style.position = 'absolute'
|
||||
// dragingItem.style.left = e.pageX + 'px'
|
||||
// dragingItem.style.top = e.pageY + 'px'
|
||||
})
|
||||
function toggleCellHightlight(e) { if (e.target.classList && e.target.classList.contains('cell')) e.target.classList.toggle('hot') }
|
||||
document.addEventListener('dragenter', toggleCellHightlight)
|
||||
document.addEventListener('dragleave', toggleCellHightlight)
|
||||
document.addEventListener('drop', e => {
|
||||
e.preventDefault()
|
||||
if (e.target.classList.contains('cell')) {
|
||||
e.target.classList.remove('hot')
|
||||
e.target.appendChild(dragingItem)
|
||||
}
|
||||
dragingItem.style.display = 'flex'
|
||||
// dragingItem.style.position = 'relative'
|
||||
// dragingItem.style.left = '0px'
|
||||
// dragingItem.style.top = '0px'
|
||||
dragingItem = null
|
||||
})
|
||||
|
||||
// Init the game
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const cell = document.createElement('span')
|
||||
cell.id = `cell-${i}`
|
||||
cell.classList.add('cell')
|
||||
grid.appendChild(cell)
|
||||
}
|
||||
shipMove(carrier, 0, 0)
|
||||
shipMove(battleship, 0, 1)
|
||||
shipMove(cruiser, 0, 2)
|
||||
shipMove(submarine, 0, 3)
|
||||
shipMove(destroyer, 0, 4)
|
106
demo/onshipgrid/style.css
Normal file
@ -0,0 +1,106 @@
|
||||
body {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: darkturquoise;
|
||||
}
|
||||
|
||||
.grid span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 1px;
|
||||
background-color: #eee;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid span.hot {
|
||||
background-color: darkturquoise;
|
||||
}
|
||||
|
||||
.ship {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
margin: -1px;
|
||||
}
|
||||
|
||||
.ship.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.carrier {
|
||||
width: 210px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.carrier.vertical {
|
||||
width: 40px;
|
||||
height: 210px;
|
||||
}
|
||||
|
||||
.battleship {
|
||||
width: 168px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.battleship.vertical {
|
||||
width: 40px;
|
||||
height: 168px;
|
||||
}
|
||||
|
||||
.cruiser,
|
||||
.submarine {
|
||||
width: 126px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.cruiser.vertical,
|
||||
.submarine.vertical {
|
||||
width: 40px;
|
||||
height: 126px;
|
||||
}
|
||||
|
||||
.destroyer {
|
||||
width: 84px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.destroyer.vertical {
|
||||
width: 40px;
|
||||
height: 84px;
|
||||
}
|
||||
|
||||
.ship span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: darkcyan;
|
||||
}
|
||||
|
||||
.ship .head {
|
||||
border-radius: 20px 0 0 20px;
|
||||
}
|
||||
|
||||
.ship .tail {
|
||||
border-radius: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.ship.vertical .head {
|
||||
border-radius: 20px 20px 0 0;
|
||||
}
|
||||
|
||||
.ship.vertical .tail {
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
35
demo/public/bs.css
Normal file
@ -0,0 +1,35 @@
|
||||
* {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#sea {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.player-grid {
|
||||
width: 430px;
|
||||
height: 430px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
background-color: darkslategray;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.player-grid .cell {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 1px;
|
||||
background-color: darkcyan;
|
||||
}
|
27
demo/public/bs.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Battleship</title>
|
||||
<script src="/socket.io/socket.io.js" defer></script>
|
||||
<script src="bs.js" defer></script>
|
||||
<link rel="stylesheet" href="bs.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Battleship</h1>
|
||||
<div id="sea">
|
||||
<div class="player">
|
||||
<h2 class="name">Plyer 1</h2>
|
||||
<div class="player-grid" id="player1-grid"></div>
|
||||
</div>
|
||||
<div class="player">
|
||||
<h2 class="name">Plyer 2</h2>
|
||||
<div class="player-grid" id="player2-grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
15
demo/public/bs.js
Normal file
@ -0,0 +1,15 @@
|
||||
(function () {
|
||||
const client = { id: "", io: io() }
|
||||
client.io.on('id', id => client.id = id)
|
||||
|
||||
const player1Grid = document.getElementById('player1-grid')
|
||||
const player2Grid = document.getElementById('player2-grid')
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const div1 = document.createElement('div')
|
||||
div1.classList.add('cell')
|
||||
player1Grid.appendChild(div1)
|
||||
const div2 = document.createElement('div')
|
||||
div2.classList.add('cell')
|
||||
player2Grid.appendChild(div2)
|
||||
}
|
||||
})()
|
28
demo/public/client.js
Normal file
@ -0,0 +1,28 @@
|
||||
const canvas = document.getElementById('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
const ColorBackgroud = '#000'
|
||||
const CanvasWidth = 1000, CanvasHeight = 500
|
||||
const client = { id: "", io: io() }
|
||||
|
||||
client.io.on('id', id => client.id = id)
|
||||
|
||||
canvas.width = CanvasWidth
|
||||
canvas.height = CanvasHeight
|
||||
ctx.fillstyle = ColorBackgroud
|
||||
ctx.fillRect(0, 0, CanvasWidth, CanvasHeight)
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
console.log(event)
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
break
|
||||
case 'ArrowDown':
|
||||
break
|
||||
case 'ArrowLeft':
|
||||
break
|
||||
case 'ArrowRight':
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
BIN
demo/public/favicon.ico
Normal file
After Width: | Height: | Size: 164 KiB |
20
demo/public/index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Battleship War</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="/socket.io/socket.io.js" defer></script>
|
||||
<script src="client.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<h1>Battleship War</h1>
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
13
demo/public/style.css
Normal file
@ -0,0 +1,13 @@
|
||||
* {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
BIN
demo/public2/favicon.ico
Normal file
After Width: | Height: | Size: 164 KiB |
37
demo/public2/index.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Battleships</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container column">
|
||||
<h1 class="text-center">Battleships</h1>
|
||||
<div class="container row">
|
||||
<input type="text" id="name" placeholder="Enter your name">
|
||||
<button id="start" style="border-left:none">Start</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const nameInput = document.querySelector("#name")
|
||||
const startButton = document.querySelector("#start")
|
||||
nameInput.value = localStorage.getItem("name") || ""
|
||||
startButton.addEventListener("click", e => {
|
||||
const name = nameInput.value.trim()
|
||||
if (name !== "") {
|
||||
localStorage.setItem("name", name)
|
||||
window.location.href = `/play.html?name=${name}`;
|
||||
} else {
|
||||
alert("Please enter your name!")
|
||||
nameInput.value = ""
|
||||
nameInput.focus()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
159
demo/public2/main.js
Normal file
@ -0,0 +1,159 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const client = io()
|
||||
const queryString = window.location.search
|
||||
const urlParams = new URLSearchParams(queryString)
|
||||
const playerName = document.querySelector('#playerName')
|
||||
playerName.innerHTML = urlParams.get('name')
|
||||
client.emit('name', playerName.innerHTML)
|
||||
|
||||
// Init the game
|
||||
const playerGrid = document.querySelector('#playerGrid')
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const cell = document.createElement('span')
|
||||
cell.id = `playerCell${i}`
|
||||
cell.classList.add('cell')
|
||||
cell.dataset.index = i
|
||||
cell.dataset.x = i % 10
|
||||
cell.dataset.y = (i - cell.dataset.x) / 10
|
||||
cell.classList.add('cell')
|
||||
playerGrid.appendChild(cell)
|
||||
}
|
||||
|
||||
const enemyGrid = document.querySelector('#enemyGrid')
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const cell = document.createElement('span')
|
||||
cell.id = `enemyCell${i}`
|
||||
cell.classList.add('cell')
|
||||
cell.dataset.index = i
|
||||
cell.dataset.x = i % 10
|
||||
cell.dataset.y = (i - cell.dataset.x) / 10
|
||||
cell.classList.add('cell')
|
||||
enemyGrid.appendChild(cell)
|
||||
}
|
||||
|
||||
const ships = document.querySelectorAll('.ship')
|
||||
const carrier = document.querySelector('.carrier') // 航空母舰
|
||||
const battleship = document.querySelector('.battleship') // 战列舰
|
||||
const cruiser = document.querySelector('.cruiser') // 巡洋舰
|
||||
const submarine = document.querySelector('.submarine') // 潜艇
|
||||
const destroyer = document.querySelector('.destroyer') // 驱逐舰
|
||||
|
||||
let playing = false
|
||||
let dragingItem = null
|
||||
|
||||
|
||||
function mod(n, w) {
|
||||
const x = n % w
|
||||
const y = (n - x) / w
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
function checkIfCollision(ox, oy, currentShip) {
|
||||
for (let ship of ships) {
|
||||
if (ship != currentShip) {
|
||||
const cell = ship.parentNode
|
||||
const index = parseInt(cell.dataset.index)
|
||||
let { x, y } = mod(index, 10)
|
||||
const vertical = ship.classList.contains('vertical')
|
||||
const length = ship.children.length
|
||||
if (vertical) {
|
||||
const tail = y + length
|
||||
for (; y < tail; y++) {
|
||||
if (ox === x && oy === y) return true
|
||||
}
|
||||
} else {
|
||||
const tail = x + length
|
||||
for (; x < tail; x++) {
|
||||
if (ox === x && oy === y) return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function shipRotate(e) {
|
||||
if (playing) return
|
||||
const currentShip = e.target.parentNode
|
||||
const length = currentShip.children.length
|
||||
const cell = currentShip.parentNode
|
||||
const index = parseInt(cell.dataset.index)
|
||||
let { x, y } = mod(index, 10)
|
||||
const vertical = currentShip.classList.contains('vertical')
|
||||
// TODO 检查碰撞
|
||||
if (vertical) {
|
||||
// 判断转为横向时是否碰撞
|
||||
const tail = x + length - 1
|
||||
if (tail >= 10) return // 船尾越界
|
||||
for (x = x + 1; x <= tail; x++) {
|
||||
if (checkIfCollision(x, y, currentShip)) return
|
||||
}
|
||||
} else {
|
||||
// 判断转为竖向时是否碰撞
|
||||
const tail = y + length - 1
|
||||
if (tail >= 10) return // 船尾越界
|
||||
for (y = y + 1; y <= tail; y++) {
|
||||
if (checkIfCollision(x, y, currentShip)) return
|
||||
}
|
||||
}
|
||||
|
||||
currentShip.classList.toggle('vertical')
|
||||
}
|
||||
|
||||
function shipMove(ship, x, y) {
|
||||
const index = 10 * y + x
|
||||
const cell = document.getElementById(`playerCell${index}`)
|
||||
const length = ship.children.length
|
||||
const vertical = ship.classList.contains('vertical')
|
||||
// TODO 检查碰撞
|
||||
if (vertical) {
|
||||
// 判断转为竖向时是否碰撞
|
||||
const tail = y + length - 1
|
||||
if (tail >= 10) return // 船尾越界
|
||||
for (let i = y + 1; i <= tail; i++) {
|
||||
if (checkIfCollision(x, i, ship)) return
|
||||
}
|
||||
} else {
|
||||
// 判断转为横向时是否碰撞
|
||||
const tail = x + length - 1
|
||||
if (tail >= 10) return // 船尾越界
|
||||
for (let i = x + 1; i <= tail; i++) {
|
||||
if (checkIfCollision(i, y, ship)) return
|
||||
}
|
||||
}
|
||||
cell.appendChild(ship)
|
||||
}
|
||||
|
||||
for (let ship of ships) ship.addEventListener('dblclick', shipRotate)
|
||||
|
||||
document.addEventListener('dragstart', e => {
|
||||
if (playing || e.target.classList && !e.target.classList.contains('ship')) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
dragingItem = e.target
|
||||
})
|
||||
document.addEventListener('dragover', e => {
|
||||
e.preventDefault()
|
||||
dragingItem.style.display = 'none'
|
||||
})
|
||||
function toggleCellHightlight(e) { if (e.target.classList && e.target.classList.contains('cell')) e.target.classList.toggle('hot') }
|
||||
document.addEventListener('dragenter', toggleCellHightlight)
|
||||
document.addEventListener('dragleave', toggleCellHightlight)
|
||||
document.addEventListener('drop', e => {
|
||||
e.preventDefault()
|
||||
if (e.target.classList.contains('cell')) {
|
||||
e.target.classList.remove('hot')
|
||||
e.target.appendChild(dragingItem)
|
||||
}
|
||||
dragingItem.style.display = 'flex'
|
||||
dragingItem = null
|
||||
})
|
||||
|
||||
shipMove(carrier, 1, 1)
|
||||
shipMove(battleship, 1, 2)
|
||||
shipMove(cruiser, 1, 3)
|
||||
shipMove(submarine, 1, 4)
|
||||
shipMove(destroyer, 1, 5)
|
||||
|
||||
})
|
61
demo/public2/play.html
Normal file
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Battleships</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container column">
|
||||
<div class="container row">
|
||||
<div class="container column">
|
||||
<div class="container row">
|
||||
<span>You: <span id="playerName">...</span></span>
|
||||
<span>Status: <span id="playerStatus">Planning</span></span>
|
||||
<span>Score: <span id="playerScore">0</span></span>
|
||||
</div>
|
||||
<div id="playerGrid" class="grid"></div>
|
||||
</div>
|
||||
<div class="container column">
|
||||
<div class="container row">
|
||||
<span>Enemy: <span id="enemyName">...</span></span>
|
||||
<span>Status: <span id="enemyStatus">...</span></span>
|
||||
<span>Score: <span id="enemyScore">0</span></span>
|
||||
</div>
|
||||
<div id="enemyGrid" class="grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ship carrier" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship battleship" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship cruiser" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship submarine" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="body"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
<div class="ship destroyer" draggable="true">
|
||||
<span class="head"></span>
|
||||
<span class="tail"></span>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="main.js" defer></script>
|
||||
</body>
|
150
demo/public2/style.css
Normal file
@ -0,0 +1,150 @@
|
||||
* {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
body,
|
||||
input,
|
||||
select,
|
||||
button {
|
||||
font-family: monospace;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
border: 1px solid #333;
|
||||
padding: 10px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
margin: 1rem 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 430px;
|
||||
height: 430px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: #003768;
|
||||
}
|
||||
|
||||
.grid span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 1px;
|
||||
background-color: #3987c9;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid span.hot {
|
||||
background-color: #7fffd4;
|
||||
}
|
||||
|
||||
.ship {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
margin: -1px;
|
||||
}
|
||||
|
||||
.ship.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.carrier {
|
||||
width: 210px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.carrier.vertical {
|
||||
width: 40px;
|
||||
height: 210px;
|
||||
}
|
||||
|
||||
.battleship {
|
||||
width: 168px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.battleship.vertical {
|
||||
width: 40px;
|
||||
height: 168px;
|
||||
}
|
||||
|
||||
.cruiser,
|
||||
.submarine {
|
||||
width: 126px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.cruiser.vertical,
|
||||
.submarine.vertical {
|
||||
width: 40px;
|
||||
height: 126px;
|
||||
}
|
||||
|
||||
.destroyer {
|
||||
width: 84px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.destroyer.vertical {
|
||||
width: 40px;
|
||||
height: 84px;
|
||||
}
|
||||
|
||||
.ship span {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
.ship .head {
|
||||
border-radius: 20px 0 0 20px;
|
||||
}
|
||||
|
||||
.ship .tail {
|
||||
border-radius: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.ship.vertical .head {
|
||||
border-radius: 20px 20px 0 0;
|
||||
}
|
||||
|
||||
.ship.vertical .tail {
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
37
demo/ship.js
Normal file
@ -0,0 +1,37 @@
|
||||
class Ship {
|
||||
constructor(name, length) {
|
||||
this.name = name
|
||||
this.length = length
|
||||
}
|
||||
}
|
||||
|
||||
class Carrier extends Ship {
|
||||
constructor() {
|
||||
super('Carrier', 5)
|
||||
}
|
||||
}
|
||||
|
||||
class Battleship extends Ship {
|
||||
constructor() {
|
||||
super('Battleship', 4)
|
||||
}
|
||||
}
|
||||
|
||||
class Destroyer extends Ship {
|
||||
constructor() {
|
||||
super('Destroyer', 3)
|
||||
}
|
||||
}
|
||||
|
||||
class Submarine extends Ship {
|
||||
constructor() {
|
||||
super('Submarine', 3)
|
||||
}
|
||||
}
|
||||
class Patrolboat extends Ship {
|
||||
constructor() {
|
||||
super('Patrolboat', 2)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Carrier, Battleship, Destroyer, Submarine, Patrolboat }
|
BIN
参考项目/846px-Battleships_Paper_Game.svg.png
Normal file
After Width: | Height: | Size: 65 KiB |
98
参考项目/BattleShip/README.md
Normal file
@ -0,0 +1,98 @@
|
||||
# Battleships!
|
||||
|
||||
A backend implementation Battleship using JavaScript.
|
||||
|
||||
## Setup
|
||||
|
||||
You will need to have the `prompt-sync` module for synchronous prompting of user input:
|
||||
|
||||
`npm install --save prompt-sync`
|
||||
|
||||
Clone the repo, navigate to the directory, and run:
|
||||
|
||||
`node app.js`
|
||||
|
||||
Simple as that! For placing ships, enter coordinates in the form `[x,y]`, for attacking ships, use `[y,x]`
|
||||
|
||||
## Design
|
||||
|
||||
Battleships uses an object-oriented approach to game design:
|
||||
|
||||
### Classes
|
||||
|
||||
**Player**
|
||||
Has a `name`, `board`, and collection of `ships`. Giving each player a board allows us to abstract the board logic to its own class, and define a set of functions that will work regardless of the state of the current player's board.
|
||||
|
||||
Player has one instance method on its prototype – `lost()` which makes use of ES6's `Array.prototype.every()` function to check if all ships in the collection are sunk.
|
||||
|
||||
**Ship**
|
||||
Ships have a `name`, `length`, and `numHits`. The ship uses 4 static methods to create instances of itself:
|
||||
|
||||
```
|
||||
Ship.aircraftCarrier = function() {
|
||||
return new Ship(5, "aircraft carrier")
|
||||
};
|
||||
```
|
||||
|
||||
This allows the `Player` class to have ships like so:
|
||||
|
||||
|
||||
```
|
||||
Player.prototype.createShips = function() {
|
||||
return (
|
||||
{
|
||||
aircraftCarrier: Ship.aircraftCarrier(),
|
||||
battleship: Ship.battleship(),
|
||||
submarine: Ship.submarine(),
|
||||
patrolBoat: Ship.patrolBoat()
|
||||
|
||||
}
|
||||
)
|
||||
};
|
||||
```
|
||||
Ships have a `hit()` method, which increments `numHits` and checks its `sunk()` method.
|
||||
|
||||
**Board**
|
||||
The board manages the placement of ships, checking if an attack hits or misses, and creation of the obfuscated board.
|
||||
|
||||
Ships are represented on the board as letters. For example, aircraft carriers are represented by the letter 'a'. The board iterates over its cells and uses a switch statement to handle a successful hit:
|
||||
|
||||
```
|
||||
if (target != 'x' && target != '~') {
|
||||
console.log("It's a hit!");
|
||||
switch (target) {
|
||||
case 'a':
|
||||
player.ships.aircraftCarrier.hit();
|
||||
break;
|
||||
case 'b':
|
||||
player.ships.battleship.hit();
|
||||
break;
|
||||
case 's':
|
||||
player.ships.submarine.hit();
|
||||
break;
|
||||
case 'p':
|
||||
player.ships.patrolBoat.hit();
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
`obfuscateBoard()` is used to display the 'upper' board of the player, which is essentially (for now) a record of successful hits. During a given players turn, they will see their opponents obfuscated board. Board obfuscation is accomplished by making a deep copy of the board and replacing anything other than an 'x' (a hit) with '~'
|
||||
|
||||
```
|
||||
Board.prototype.obfuscateBoard = function() {
|
||||
let deepCopy = JSON.parse(JSON.stringify(this.cells));
|
||||
deepCopy.forEach( (row, i) => {
|
||||
row.forEach( (el, j) => {
|
||||
if (el !== 'x' && el !== '~') {
|
||||
deepCopy[i][j] = '~';
|
||||
}
|
||||
});
|
||||
});
|
||||
return deepCopy;
|
||||
}
|
||||
```
|
||||
|
||||
**app**
|
||||
The game driver, app.js, is not actually a class. It is a collection of functions mainly used to set up the game and maintain the rules with validation of coordinates, checking if the game is over, etc. It instantiates two players and then calls `gameSetup()` once for each player followed by `playGame()`
|
||||
|
||||
Validation is tricky. There are many ways for a user to give bad input. `validCoords()` stops four behaviors: placing ships diagonally, placing ships out of bounds, placing ships on top of one another, placing ships in a space that is too small or large for the given ship.
|
113
参考项目/BattleShip/app.js
Normal file
@ -0,0 +1,113 @@
|
||||
const Player = require('./player.js')
|
||||
const Board = require('./board.js')
|
||||
const readline = require('readline')
|
||||
var prompt = require('prompt-sync')({
|
||||
autocomplete: null,
|
||||
})
|
||||
|
||||
var player1 = new Player("Player 1")
|
||||
var player2 = new Player("Player 2")
|
||||
var currentPlayer = player1
|
||||
|
||||
function gameSetup(player) {
|
||||
console.log(`${player.name}, time to place your ships.`)
|
||||
for (var el in player.ships) {
|
||||
getInput(player.ships[el], player.ships[el].name, player)
|
||||
}
|
||||
}
|
||||
|
||||
function getInput(ship, shipName, player) {
|
||||
var letter = shipName[0]
|
||||
var pair1 = prompt(`Place your ${shipName}, start position `)
|
||||
var pair2 = prompt(`Place your ${shipName}, end position `)
|
||||
while (!validCoords(pair1, pair2, player, ship)) {
|
||||
var pair1 = prompt(`Place your ${shipName}, start position `)
|
||||
var pair2 = prompt(`Place your ${shipName}, end position `)
|
||||
}
|
||||
player.board.placeShip(ship, [pair1, pair2], letter)
|
||||
}
|
||||
|
||||
function validCoords(p1, p2, player, ship) {
|
||||
var x1 = parseInt(p1[1])
|
||||
var y1 = parseInt(p1[3])
|
||||
var x2 = parseInt(p2[1])
|
||||
var y2 = parseInt(p2[3])
|
||||
|
||||
// Ensure coordinates in bounds.
|
||||
if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0) {
|
||||
console.log("Coordinates out of bounds")
|
||||
return false
|
||||
}
|
||||
|
||||
if (x1 > 6 || x2 > 6 || y1 > 6 || y2 > 6) {
|
||||
console.log("Coordinates out of bounds")
|
||||
return false
|
||||
}
|
||||
|
||||
// If both x coords are the same, then the y coords must be different, IE, no diagonal ships.
|
||||
if (!(((x1 != x2) && (y1 === y2)) || ((x1 === x2) && (y1 != y2)))) {
|
||||
console.log("Ships cannot be placed diagonally.")
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// Ensure space isn't already populated
|
||||
if (x1 != x2) {
|
||||
var len = Math.abs(x1 - y1)
|
||||
|
||||
for (var i = x1; i < x2; i++) {
|
||||
if (player.board.cells[y1][i] != '~') {
|
||||
console.log("A ship already exists there.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the distance between the two coordinates is big enough to fit the whole ship.
|
||||
if (Math.abs(x1 - x2) != ship.length) {
|
||||
console.log("Space is too small or too large to fit ship.")
|
||||
return false
|
||||
}
|
||||
|
||||
} else {
|
||||
var len = Math.abs(y1 - y2)
|
||||
|
||||
for (var i = y1; i < y2; i++) {
|
||||
if (player.board.cells[i][x1] != '~') {
|
||||
console.log("A ship alredy exists there.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(y1 - y2) != ship.length) {
|
||||
console.log("Space is too small or too large to fit ship.")
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function getAttackCoords() {
|
||||
return prompt('Enter attack target in the form [y,x] ')
|
||||
}
|
||||
|
||||
function oppositePlayer() {
|
||||
var other = (currentPlayer === player1 ? player2 : player1)
|
||||
return other
|
||||
}
|
||||
|
||||
function playGame() {
|
||||
while (!currentPlayer.lost()) {
|
||||
console.log(`${currentPlayer.name}, it's your turn!`)
|
||||
console.log("Your opponent's board:")
|
||||
console.log(oppositePlayer().board.obfuscateBoard())
|
||||
var attackCoords = getAttackCoords()
|
||||
oppositePlayer().board.hit(attackCoords, oppositePlayer())
|
||||
currentPlayer = oppositePlayer()
|
||||
}
|
||||
console.log(`${currentPlayer.name} loses!`)
|
||||
}
|
||||
|
||||
gameSetup(player1)
|
||||
gameSetup(player2)
|
||||
playGame()
|
94
参考项目/BattleShip/board.js
Normal file
@ -0,0 +1,94 @@
|
||||
"use strict"
|
||||
|
||||
function Board(name) {
|
||||
this.cells = this.makeBoard(7, 7)
|
||||
this.name = name
|
||||
this.placeShip = this.placeShip.bind(this)
|
||||
this.obfuscateBoard = this.obfuscateBoard.bind(this)
|
||||
this.setDefault = this.setDefault.bind(this)
|
||||
this.setDefault()
|
||||
}
|
||||
|
||||
Board.prototype.makeBoard = function (length) {
|
||||
let arr = new Array(length || 0),
|
||||
i = length
|
||||
if (arguments.length > 1) {
|
||||
let args = Array.prototype.slice.call(arguments, 1)
|
||||
while (i--) arr[length - 1 - i] = this.makeBoard.apply(this, args)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
Board.prototype.placeShip = function (ship, coordindates, shipSym) {
|
||||
let x1 = parseInt(coordindates[0][1])
|
||||
let y1 = parseInt(coordindates[0][3])
|
||||
let x2 = parseInt(coordindates[1][1])
|
||||
let y2 = parseInt(coordindates[1][3])
|
||||
|
||||
// Check if ship extends vertically or horizontally.
|
||||
if (x1 != x2) {
|
||||
let len = Math.abs(x1 - x2)
|
||||
for (let i = x1; i < x2; i++) {
|
||||
this.cells[y1][i] = shipSym
|
||||
}
|
||||
} else {
|
||||
let len = Math.abs(y1 - y2)
|
||||
for (let i = y1; i < y2; i++) {
|
||||
this.cells[i][x1] = shipSym
|
||||
}
|
||||
}
|
||||
console.log(this.cells)
|
||||
}
|
||||
|
||||
// 5 possible outcomes: hit, miss, repeat hit, sunk, victory.
|
||||
Board.prototype.hit = function (coordindates, player) {
|
||||
let x1 = parseInt(coordindates[1])
|
||||
let y1 = parseInt(coordindates[3])
|
||||
let target = this.cells[x1][y1]
|
||||
|
||||
if (target != 'x' && target != '~') {
|
||||
console.log("It's a hit!")
|
||||
switch (target) {
|
||||
case 'a':
|
||||
player.ships.aircraftCarrier.hit()
|
||||
break
|
||||
case 'b':
|
||||
player.ships.battleship.hit()
|
||||
break
|
||||
case 's':
|
||||
player.ships.submarine.hit()
|
||||
break
|
||||
case 'p':
|
||||
player.ships.patrolBoat.hit()
|
||||
break
|
||||
}
|
||||
// Mark cell so we know that part of the ship has been hit.
|
||||
this.cells[x1][y1] = 'x'
|
||||
} else if (target === 'x') {
|
||||
console.log("You already attacked that spot!")
|
||||
} else {
|
||||
console.log("Missfire!")
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the board with water tiles.
|
||||
Board.prototype.setDefault = function () {
|
||||
this.cells.forEach(row => {
|
||||
row.fill('~')
|
||||
})
|
||||
}
|
||||
|
||||
// Display board only showing spots where the player has successfully hit the opponent.
|
||||
Board.prototype.obfuscateBoard = function () {
|
||||
let deepCopy = JSON.parse(JSON.stringify(this.cells))
|
||||
deepCopy.forEach((row, i) => {
|
||||
row.forEach((el, j) => {
|
||||
if (el !== 'x' && el !== '~') {
|
||||
deepCopy[i][j] = '~'
|
||||
}
|
||||
})
|
||||
})
|
||||
return deepCopy
|
||||
}
|
||||
|
||||
module.exports = Board
|
31257
参考项目/BattleShip/bundle.js
Normal file
30
参考项目/BattleShip/player.js
Normal file
@ -0,0 +1,30 @@
|
||||
const Ship = require('./ship.js');
|
||||
const Board = require('./board.js');
|
||||
|
||||
function Player(name) {
|
||||
this.name = name;
|
||||
this.board = new Board(this.name);
|
||||
this.ships = this.createShips();
|
||||
this.lost = this.lost.bind(this);
|
||||
}
|
||||
|
||||
Player.prototype.createShips = function() {
|
||||
return (
|
||||
{
|
||||
aircraftCarrier: Ship.aircraftCarrier(),
|
||||
// battleship: Ship.battleship(),
|
||||
// submarine: Ship.submarine(),
|
||||
// patrolBoat: Ship.patrolBoat()
|
||||
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
Player.prototype.lost = function() {
|
||||
var bool = Object.keys(this.ships).every( (ship) => {
|
||||
return this.ships[ship].sunk()
|
||||
});
|
||||
return bool;
|
||||
};
|
||||
|
||||
module.exports = Player
|
38
参考项目/BattleShip/ship.js
Normal file
@ -0,0 +1,38 @@
|
||||
function Ship(length, name) {
|
||||
this.length = length;
|
||||
this.name = name;
|
||||
this.numHits = 0;
|
||||
}
|
||||
|
||||
Ship.aircraftCarrier = function() {
|
||||
return new Ship(5, "aircraft carrier")
|
||||
};
|
||||
|
||||
Ship.submarine = function() {
|
||||
return new Ship(4, "submarine")
|
||||
};
|
||||
|
||||
Ship.battleship = function() {
|
||||
return new Ship(3, "battleship")
|
||||
};
|
||||
|
||||
Ship.patrolBoat = function() {
|
||||
return new Ship(2, "patrol boat")
|
||||
};
|
||||
|
||||
Ship.prototype.hit = function() {
|
||||
this.numHits += 1;
|
||||
if (this.sunk()) {
|
||||
console.log(`You sunk your opponent's ${this.name}!`)
|
||||
}
|
||||
};
|
||||
|
||||
Ship.prototype.sunk = function() {
|
||||
if (this.numHits === this.length) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Ship;
|
10
参考项目/battleboat/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
battleboat
|
||||
==========
|
||||
|
||||
A JavaScript AI that beats humans at battleship.
|
||||
|
||||
Play the game here: [https://billmei.github.io/battleboat](https://billmei.github.io/battleboat)
|
||||
|
||||
If you've forked a copy of this repo or are playing around with the code on the `https://billmei.github.io/battleboat` page, please be nice and don't hack the `Stats` object. I'm using Google Analytics to collect info about the AI's win/loss percentage in order to improve the bot, so if you do look around, I kindly ask that you don't give it bad data. Thanks :)
|
||||
|
||||
If you want to try stuff out, run `setDebug(true);` in the console before doing anything. You'll also get access to some cool features.
|
1371
参考项目/battleboat/battleboat.js
Normal file
BIN
参考项目/battleboat/img/cross-icon.png
Normal file
After Width: | Height: | Size: 187 B |
8
参考项目/battleboat/img/cross-icon.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" x1="0" y1="0" x2="16" y2="16"/>
|
||||
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" x1="0" y1="16" x2="16" y2="0"/>
|
||||
</svg>
|
After Width: | Height: | Size: 685 B |
BIN
参考项目/battleboat/img/crosshair.png
Normal file
After Width: | Height: | Size: 639 B |
BIN
参考项目/battleboat/img/favicon.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
88
参考项目/battleboat/index.html
Normal file
@ -0,0 +1,88 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,maximum-scale=1">
|
||||
<title>Battleboat.js - A JavaScript AI that beats humans at battleship.</title>
|
||||
<meta name="google" content="notranslate">
|
||||
<link rel="icon" type="image/png" href="img/favicon.png" />
|
||||
<link href="styles.css" rel="stylesheet" media="all" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Battleboat.js</h1>
|
||||
<p class="tagline">A JavaScript AI that beats humans at battleship.</p>
|
||||
<p>How to play</p>
|
||||
<ol class="instructions">
|
||||
<li id="step1">Select your ships from the left-hand side</li>
|
||||
<li id="step2">Place your ships on your map</li>
|
||||
<li id="step3">Click on the cells of the enemy's map<br>
|
||||
to find and destroy all five enemy ships</li>
|
||||
<li id="step4">The computer will fire on your ships<br>
|
||||
immediately after you fire on its ships.</li>
|
||||
</ol>
|
||||
<div class="game-container">
|
||||
<div id="restart-sidebar" class="hidden">
|
||||
<h2>Try Again</h2>
|
||||
<button id="restart-game">Restart Game</button>
|
||||
</div>
|
||||
<div id="roster-sidebar">
|
||||
<h2>Place Your Ships</h2>
|
||||
<ul class="fleet-roster">
|
||||
<li id="patrolboat">Patrol Boat</li>
|
||||
<li id="submarine">Submarine</li>
|
||||
<li id="destroyer">Destroyer</li>
|
||||
<li id="battleship">Battleship</li>
|
||||
<li id="carrier">Aircraft Carrier</li>
|
||||
</ul>
|
||||
<button id="rotate-button" data-direction="0">Rotate Ship</button>
|
||||
<button id="start-game" class="hidden">Start Game</button>
|
||||
<button id="place-randomly" class="hidden">Place Randomly and Start</button>
|
||||
</div>
|
||||
<div id="stats-sidebar">
|
||||
<h2>Stats</h2>
|
||||
<p><strong>Games Won</strong></p>
|
||||
<p id="stats-wins">0 of 0</p>
|
||||
<p><strong>Accuracy</strong></p>
|
||||
<p id="stats-accuracy">0%</p>
|
||||
<button id="reset-stats">Reset Stats</button>
|
||||
<button id="prob-heatmap" class="hidden">Show Probability Heatmap</button>
|
||||
</div>
|
||||
<div class="grid-container">
|
||||
<h2>Your Fleet</h2>
|
||||
<div class="grid human-player"><span class="no-js">Please enable JavaScript to play this game</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-container">
|
||||
<h2>Enemy Fleet</h2>
|
||||
<div class="grid computer-player"><span class="no-js">Please enable JavaScript to play this game</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Don't change this variable.
|
||||
var DEBUG_MODE = localStorage.getItem('DEBUG_MODE') === 'true';
|
||||
// To turn DEBUG_MODE on, run `setDebug(true);` in the console.
|
||||
if (!DEBUG_MODE) {
|
||||
(function (i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
|
||||
(i[r].q = i[r].q || []).push(arguments)
|
||||
}, i[r].l = 1 * new Date(); a = s.createElement(o),
|
||||
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
|
||||
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
|
||||
ga('create', 'UA-10730961-10', 'auto');
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="battleboat.js"></script>
|
||||
<span class="prefetch" id="prefetch1"></span>
|
||||
<span class="prefetch" id="prefetch2"></span>
|
||||
<span class="prefetch" id="prefetch3"></span>
|
||||
</body>
|
||||
|
||||
</html>
|
398
参考项目/battleboat/styles.css
Normal file
@ -0,0 +1,398 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: #ddd;
|
||||
min-width: 860px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
li,
|
||||
span {
|
||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #222222;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||
font-size: 1.2em;
|
||||
color: #EEEEEE;
|
||||
background-color: #25567B;
|
||||
border: none;
|
||||
margin: 1em auto 0 auto;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #99C2E1;
|
||||
}
|
||||
|
||||
#start-game,
|
||||
#place-randomly,
|
||||
#restart-game {
|
||||
position: relative;
|
||||
color: #FFFFFF;
|
||||
background-color: #FF9200;
|
||||
}
|
||||
|
||||
#start-game:hover,
|
||||
#place-randomly:hover,
|
||||
#restart-game:hover {
|
||||
background-color: #FFB655;
|
||||
}
|
||||
|
||||
#start-game:active,
|
||||
#place-randomly:active,
|
||||
#restart-game:active {
|
||||
background-color: #FFCE8E;
|
||||
}
|
||||
|
||||
#prefetch1 {
|
||||
background: url('../img/cross-icon.svg');
|
||||
}
|
||||
|
||||
#prefetch2 {
|
||||
background: url('../img/cross-icon.png');
|
||||
}
|
||||
|
||||
#prefetch3 {
|
||||
background: url('../img/crosshair.png');
|
||||
}
|
||||
|
||||
.prefetch {
|
||||
background-repeat: no-repeat;
|
||||
background-position: -9999px -9999px;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.instructions {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
margin: 0 auto 3em auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 870px;
|
||||
text-align: center;
|
||||
margin: 20px auto 100px auto;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#roster-sidebar,
|
||||
#stats-sidebar,
|
||||
#restart-sidebar {
|
||||
width: 150px;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
background-color: #EEEEEE;
|
||||
position: absolute;
|
||||
top: 4.5em;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#roster-sidebar,
|
||||
#restart-sidebar {
|
||||
left: -200px;
|
||||
}
|
||||
|
||||
#stats-sidebar {
|
||||
right: -200px;
|
||||
}
|
||||
|
||||
#roster-sidebar h2,
|
||||
#stats-sidebar h2,
|
||||
#restart-sidebar h2 {
|
||||
margin: 0 0 1em 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fleet-roster {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.fleet-roster,
|
||||
button {
|
||||
opacity: 1;
|
||||
-webkit-transition: opacity 0.5s ease-out;
|
||||
-o-transition: opacity 0.5s ease-out;
|
||||
transition: opacity 0.5s ease-out;
|
||||
}
|
||||
|
||||
.fleet-roster li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.fleet-roster li:hover {
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fleet-roster .placing {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fleet-roster .placed {
|
||||
visibility: hidden;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.2s ease-in;
|
||||
-o-transition: all 0.2s ease-in;
|
||||
transition: all 0.2s ease-in;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.5s ease-in;
|
||||
-o-transition: opacity 0.5s ease-in;
|
||||
transition: opacity 0.5s ease-in;
|
||||
z-index: -20;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
width: 430px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.grid-container h2 {
|
||||
width: 430px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
padding: 5px;
|
||||
height: 420px;
|
||||
width: 420px;
|
||||
background-color: #25567B;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.grid-container:last-child {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.no-js {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin: 100px auto;
|
||||
display: inline-block;
|
||||
color: #EEEEEE;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
vertical-align: top;
|
||||
/*clears the vertical space between rows*/
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: inline-block;
|
||||
background-color: #99C2E1;
|
||||
margin: 1px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.grid-cell:hover {
|
||||
cursor: pointer;
|
||||
/* Fallback for IE */
|
||||
background-color: #66A3D2;
|
||||
}
|
||||
|
||||
.human-player .grid-cell:hover {
|
||||
background-color: #99C2E1;
|
||||
}
|
||||
|
||||
.computer-player .grid-cell:hover,
|
||||
.computer-player:hover {
|
||||
cursor: url('../img/crosshair.png') 16 16, crosshair;
|
||||
}
|
||||
|
||||
.grid-ship,
|
||||
.human-player .grid-ship:hover {
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.grid-miss,
|
||||
.grid-miss:hover,
|
||||
.human-player .grid-miss:hover {
|
||||
background-color: #FFFFFF;
|
||||
background-image: url('../img/cross-icon.png');
|
||||
/* Fallback */
|
||||
background-image: url('../img/cross-icon.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.grid-hit,
|
||||
.grid-hit:hover,
|
||||
.human-player .grid-hit:hover {
|
||||
background-color: #F60018;
|
||||
background-image: url('../img/cross-icon.png');
|
||||
/* Fallback */
|
||||
background-image: url('../img/cross-icon.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.grid-sunk,
|
||||
.grid-sunk:hover,
|
||||
.human-player .grid-sunk:hover {
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
overflow: visible;
|
||||
/* Bugfix for IE */
|
||||
}
|
||||
|
||||
.highlight:before {
|
||||
content: "\2193";
|
||||
font-weight: bold;
|
||||
font-size: 75px;
|
||||
color: #FF9200;
|
||||
text-shadow: 0 0 5px #FF9200;
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
top: -100px;
|
||||
left: 50%;
|
||||
margin-left: -50px;
|
||||
-webkit-animation: highlight 1.5s infinite;
|
||||
-o-animation: highlight 1.5s infinite;
|
||||
animation: highlight 1.5s infinite;
|
||||
}
|
||||
|
||||
.current-step {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media(max-width: 1300px) {
|
||||
.container {
|
||||
width: 440px;
|
||||
}
|
||||
|
||||
.grid-container:last-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes highlight {
|
||||
0% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateY(-20px);
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes highlight {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes highlight {
|
||||
0% {
|
||||
-o-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
-o-transform: translateY(-20px);
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-o-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes highlight {
|
||||
0% {
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-o-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateY(-20px);
|
||||
-ms-transform: translateY(-20px);
|
||||
-o-transform: translateY(-20px);
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-o-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
37
参考项目/bs/index.html
Normal file
@ -0,0 +1,37 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>BattleShip</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div class="board">
|
||||
<div class="displays">
|
||||
<div class="top">
|
||||
<ul class="grid"></ul>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<ul class="grid"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="topPanel">
|
||||
<div class="layout">
|
||||
<div class='buttons one'>One-Player</div>
|
||||
<div class='buttons multi'>Two-Player</div>
|
||||
<div class='buttons options'>Options</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='console'>
|
||||
<span class='text'></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
751
参考项目/bs/script.js
Normal file
@ -0,0 +1,751 @@
|
||||
// Variables
|
||||
var playerFleet, cpuFleet
|
||||
var attemptedHits = []
|
||||
|
||||
// Object Constructors
|
||||
function Fleet(name) {
|
||||
this.name = name
|
||||
this.shipDetails = [{ "name": "carrier", "length": 5 },
|
||||
{ "name": "battleship", "length": 4 },
|
||||
{ "name": "cruiser", "length": 3 },
|
||||
{ "name": "destroyer", "length": 3 },
|
||||
{ "name": "frigate", "length": 2 }]
|
||||
this.numOfShips = this.shipDetails.length
|
||||
this.ships = []
|
||||
this.currentShipSize = 0
|
||||
this.currentShip = 0
|
||||
this.initShips = function () {
|
||||
for (var i = 0; i < this.numOfShips; i++) {
|
||||
this.ships[i] = new Ship(this.shipDetails[i].name)
|
||||
this.ships[i].length = this.shipDetails[i].length
|
||||
}
|
||||
}
|
||||
this.removeShip = function (pos) {
|
||||
this.numOfShips--
|
||||
$(".text").text(output.sunk(this.name, this.ships[pos].name))
|
||||
if (this == playerFleet) bot.sizeOfShipSunk = this.ships[pos].length
|
||||
this.ships.splice(pos, 1)
|
||||
if (this.ships.length == 0) {
|
||||
$(".text").text(output.lost(this.name))
|
||||
}
|
||||
return true
|
||||
}
|
||||
this.shipHit = function (ship_name) {
|
||||
$(".text").text(output.hit(this.name))
|
||||
return true
|
||||
}
|
||||
this.checkIfHit = function (point) {
|
||||
for (var i = 0; i < this.numOfShips; i++) {
|
||||
if (this.ships[i].checkLocation(point)) {
|
||||
this.ships[i].getRidOf(this.ships[i].hitPoints.indexOf(point))
|
||||
if (this.ships[i].hitPoints == 0) return this.removeShip(i)
|
||||
else return this.shipHit(this.ships[i].name)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function Ship(name) {
|
||||
this.name = name
|
||||
this.length = 0
|
||||
this.hitPoints = []
|
||||
this.populateHorzHits = function (start) {
|
||||
for (var i = 0; i < this.length; i++, start++) {
|
||||
this.hitPoints[i] = start
|
||||
}
|
||||
}
|
||||
this.populateVertHits = function (start) {
|
||||
for (var i = 0; i < this.length; i++, start += 10) {
|
||||
this.hitPoints[i] = start
|
||||
}
|
||||
}
|
||||
this.checkLocation = function (loc) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (this.hitPoints[i] == loc) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
this.getRidOf = function (pos) {
|
||||
this.hitPoints.splice(pos, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Console obj
|
||||
var output = {
|
||||
"welcome": " > Welcome to BattleShip. Use the menu above to get started.",
|
||||
"not": " > This option is not currently available.",
|
||||
"player1": " > Would you like to place your own ships or have the computer randomly do it for you?",
|
||||
"self": " > Use the mouse and the Horizontal and Vertial buttons to place your ships on the bottom grid.",
|
||||
"overlap": " > You can not overlap ships. Please try again.",
|
||||
"start": " > Use the mouse to fire on the top grid. Good Luck!",
|
||||
placed: function (name) { return " > Your " + name + " been placed." },
|
||||
hit: function (name, type) { return " > " + name + "'s ship was hit." },
|
||||
miss: function (name) { return " > " + name + " missed!" },
|
||||
sunk: function (user, type) { return " > " + user + "'s " + type + " was sunk!" },
|
||||
lost: function (name) { return " > " + name + " has lost his fleet!! Game Over." },
|
||||
}
|
||||
|
||||
// Objects for playing the game and bot for playing the computer
|
||||
var topBoard = {
|
||||
allHits: [],
|
||||
highlight: function (square) {
|
||||
$(square).addClass("target").off("mouseleave").on("mouseleave", function () {
|
||||
$(this).removeClass("target")
|
||||
})
|
||||
|
||||
$(square).off("click").on("click", function () {
|
||||
if (!($(this).hasClass("used"))) {
|
||||
$(this).removeClass("target").addClass("used")
|
||||
var num = parseInt($(this).attr("class").slice(15))
|
||||
var bool = cpuFleet.checkIfHit(num)
|
||||
if (false == bool) {
|
||||
$(".text").text(output.miss("You"))
|
||||
$(this).children().addClass("miss")
|
||||
} else $(this).children().addClass("hit")
|
||||
$(".top").find(".points").off("mouseenter").off("mouseover").off("mouseleave").off("click")
|
||||
// Check if it's the end of the game
|
||||
if (cpuFleet.ships.length == 0) {
|
||||
$(".top").find(".points").off("mouseenter").off("mouseover").off("mouseleave").off("click")
|
||||
|
||||
} else setTimeout(bot.select, 800)
|
||||
} // end of if
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
var bottomBoard = {
|
||||
currentHits: [],
|
||||
checkAttempt: function (hit) {
|
||||
if (playerFleet.checkIfHit(hit)) {
|
||||
// Insert hit into an array for book keeping
|
||||
bottomBoard.currentHits.push(hit)
|
||||
if (this.currentHits.length > 1) bot.prev_hit = true
|
||||
// display hit on the grid
|
||||
$(".bottom").find("." + hit).children().addClass("hit")
|
||||
if (bottomBoard.hasShipBeenSunk()) {
|
||||
// clear flags
|
||||
bot.hunting = bot.prev_hit = false
|
||||
if (bot.sizeOfShipSunk == bottomBoard.currentHits.length) {
|
||||
bot.num_misses = bot.back_count = bot.nextMove.length = bottomBoard.currentHits.length = bot.sizeOfShipSunk = bot.currrent = 0
|
||||
} else {
|
||||
bot.special = bot.case1 = true
|
||||
}
|
||||
// check for special cases
|
||||
if (bot.specialHits.length > 0) bot.special = true
|
||||
// check for end of game.
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
$(".bottom").find("." + hit).children().addClass("miss")
|
||||
bot.current = bottomBoard.currentHits[0]
|
||||
bot.prev_hit = false
|
||||
if (bottomBoard.currentHits.length > 1) {
|
||||
bot.back = true
|
||||
bot.num_misses++
|
||||
}
|
||||
if (bot.case2) {
|
||||
bot.special = true
|
||||
bot.case2 = false
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
hasShipBeenSunk: function () {
|
||||
if (bot.sizeOfShipSunk > 0) return true
|
||||
else return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var bot = {
|
||||
back: false,
|
||||
hunting: false,
|
||||
prev_hit: false,
|
||||
first_hit: false,
|
||||
special: false,
|
||||
case1: false,
|
||||
case2: false,
|
||||
num_misses: 0,
|
||||
back_count: 0,
|
||||
randPool: [],
|
||||
nextMove: [],
|
||||
attempted: [],
|
||||
specialHits: [],
|
||||
direction: "",
|
||||
current: 0,
|
||||
numAttemptsAfterHit: 0,
|
||||
sizeOfShipSunk: 0,
|
||||
randomGen: function (size) {
|
||||
return Math.floor(Math.random() * size)
|
||||
},
|
||||
select: function () {
|
||||
if (bot.hunting) {
|
||||
bot.battleLogic()
|
||||
} else if (bot.special) {
|
||||
bot.specialCase()
|
||||
} else {
|
||||
// grab a random number from the pool and increase attempts
|
||||
bot.current = bot.randPool[bot.randomGen(bot.randPool.length)]
|
||||
bot.attempted.push(bot.current)
|
||||
bot.first_hit = true
|
||||
// remove current guess from the random pool and check if hit
|
||||
bot.removeGuess(bot.randPool.indexOf(bot.current))
|
||||
bot.hunting = bottomBoard.checkAttempt(bot.current)
|
||||
}
|
||||
setTimeout(highlightBoard(), 50)
|
||||
},
|
||||
|
||||
removeGuess: function (index) {
|
||||
bot.randPool.splice(index, 1)
|
||||
},
|
||||
|
||||
battleLogic: function () {
|
||||
if (bot.first_hit) {
|
||||
bot.createMoves()
|
||||
bot.first_hit = false
|
||||
}
|
||||
|
||||
if (bot.num_misses > 1) {
|
||||
bot.specialCase()
|
||||
} else if (bot.back) {
|
||||
bot.back = false
|
||||
bot.backy()
|
||||
bot.deployHit(bot.current)
|
||||
} else if (bot.prev_hit) {
|
||||
bot.continueHits()
|
||||
bot.deployHit(bot.current)
|
||||
console.log(bot.prev_hit)
|
||||
} else {
|
||||
bot.direction = bot.nextMove.pop()
|
||||
console.log(bot.direction + " " + bot.current)
|
||||
bot.getNumericalDirection(bot.direction)
|
||||
bot.prev_hit = bot.deployHit(bot.current)
|
||||
console.log(bot.prev_hit)
|
||||
}
|
||||
},
|
||||
|
||||
deployHit: function (hit) {
|
||||
if (bot.special) {
|
||||
bot.specialCase()
|
||||
} else {
|
||||
bot.attempted.push(hit)
|
||||
bot.removeGuess(bot.randPool.indexOf(hit))
|
||||
return bottomBoard.checkAttempt(hit)
|
||||
}
|
||||
},
|
||||
|
||||
createMoves: function () {
|
||||
if (bot.current == 1) {
|
||||
bot.getRandomMoves(["right", "down"])
|
||||
}
|
||||
else if (bot.current == 10) {
|
||||
bot.getRandomMoves(["left", "down"])
|
||||
}
|
||||
else if (bot.current == 91) {
|
||||
bot.getRandomMoves(["up", "right"])
|
||||
}
|
||||
else if (bot.current == 100) {
|
||||
bot.getRandomMoves(["left", "up"])
|
||||
}
|
||||
else if (!(bot.current % 10)) {
|
||||
bot.getRandomMoves(["up", "down", "left"])
|
||||
}
|
||||
else if (bot.current < 10) {
|
||||
bot.getRandomMoves(["right", "down", "left"])
|
||||
}
|
||||
else if (bot.current % 10 == 1) {
|
||||
bot.getRandomMoves(["up", "right", "down"])
|
||||
}
|
||||
else if (bot.current > 91) {
|
||||
bot.getRandomMoves(["up", "right", "left"])
|
||||
}
|
||||
else {
|
||||
bot.getRandomMoves(["up", "right", "down", "left"])
|
||||
}
|
||||
},
|
||||
|
||||
getRandomMoves: function (possibleMoves) {
|
||||
while (possibleMoves.length != 0) {
|
||||
// pick a random direction
|
||||
var dir = bot.randomGen(possibleMoves.length)
|
||||
// Go Up
|
||||
if (possibleMoves[dir] == "up") {
|
||||
if (bot.randPool.some(function (x) { return x == bot.current - 10 })) {
|
||||
bot.nextMove.push("up")
|
||||
}
|
||||
}
|
||||
// Go right
|
||||
if (possibleMoves[dir] == "right") {
|
||||
if (bot.randPool.some(function (x) { return x == bot.current + 1 })) {
|
||||
bot.nextMove.push("right")
|
||||
}
|
||||
}
|
||||
// Go down
|
||||
if (possibleMoves[dir] == "down") {
|
||||
if (bot.randPool.some(function (x) { return x == bot.current + 10 })) {
|
||||
bot.nextMove.push("down")
|
||||
}
|
||||
}
|
||||
// Go left
|
||||
if (possibleMoves[dir] == "left") {
|
||||
if (bot.randPool.some(function (x) { return x == bot.current - 1 })) {
|
||||
bot.nextMove.push("left")
|
||||
}
|
||||
}
|
||||
possibleMoves.splice(dir, 1)
|
||||
}
|
||||
},
|
||||
|
||||
getNumericalDirection: function (dir) {
|
||||
if (dir == "up") bot.current -= 10
|
||||
if (dir == "right") bot.current += 1
|
||||
if (dir == "down") bot.current += 10
|
||||
if (dir == "left") bot.current -= 1
|
||||
console.log(bot.current + " attempted " + bot.attempted)
|
||||
// check if already used
|
||||
if (bot.attempted.some(function (x) { return x == bot.current }) && bot.specialHits.length == 0) {
|
||||
bot.current = bottomBoard.currentHits[0]
|
||||
if (bot.back_count > 1) bot.special = true
|
||||
else bot.backy()
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
continueHits: function () {
|
||||
console.log("cont " + bot.direction)
|
||||
if (bot.direction == "up") {
|
||||
if (bot.checkLocation("up")) {
|
||||
bot.direction = "down"
|
||||
return bot.getNumericalDirection(bot.direction)
|
||||
} else return bot.getNumericalDirection(bot.direction)
|
||||
}
|
||||
if (bot.direction == "right") {
|
||||
if (bot.checkLocation("right")) {
|
||||
bot.direction = "left"
|
||||
return bot.getNumericalDirection(bot.direction)
|
||||
} else return bot.getNumericalDirection(bot.direction)
|
||||
}
|
||||
if (bot.direction == "down") {
|
||||
if (bot.checkLocation("down")) {
|
||||
bot.direction = "up"
|
||||
return bot.getNumericalDirection(bot.direction)
|
||||
} else return bot.getNumericalDirection(bot.direction)
|
||||
}
|
||||
if (bot.direction == "left") {
|
||||
if (bot.checkLocation("left")) {
|
||||
bot.direction = "right"
|
||||
return bot.getNumericalDirection(bot.direction)
|
||||
} else return bot.getNumericalDirection(bot.direction)
|
||||
}
|
||||
},
|
||||
|
||||
backy: function () {
|
||||
bot.back_count++
|
||||
if (bot.direction == "up") {
|
||||
bot.direction = "down"
|
||||
return bot.continueHits()
|
||||
}
|
||||
if (bot.direction == "right") {
|
||||
bot.direction = "left"
|
||||
return bot.continueHits()
|
||||
}
|
||||
if (bot.direction == "down") {
|
||||
bot.direction = "up"
|
||||
return bot.continueHits()
|
||||
}
|
||||
if (bot.direction == "left") {
|
||||
bot.direction = "right"
|
||||
return bot.continueHits()
|
||||
}
|
||||
},
|
||||
|
||||
checkLocation: function (dir) {
|
||||
if (dir == "up") {
|
||||
if (bot.current < 11) return true
|
||||
}
|
||||
if (dir == "right") {
|
||||
if (bot.current % 10 == 0) return true
|
||||
}
|
||||
if (dir == "down") {
|
||||
if (bot.current > 90) return true
|
||||
}
|
||||
if (dir == "left") {
|
||||
if (bot.current % 10 == 1) return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
specialCase: function () {
|
||||
bot.num_misses = bot.back_count = bot.nextMove.length = 0
|
||||
if (bot.case1) {
|
||||
bot.prev_hit = true
|
||||
if (bot.getNewCurrent(bot.direction)) {
|
||||
bottomBoard.currentHits.length = 0
|
||||
bottomBoard.currentHits.push(bot.current)
|
||||
bot.first_hit = true
|
||||
bot.prev_hit = false
|
||||
}
|
||||
bot.special = bot.case1 = bot.back = false
|
||||
bot.hunting = true
|
||||
bot.sizeOfShipSunk = 0
|
||||
bot.battleLogic()
|
||||
} else {
|
||||
if (bot.specialHits.length == 0) {
|
||||
for (var i = 0; i < bottomBoard.currentHits.length; i++) {
|
||||
bot.specialHits.push(bottomBoard.currentHits[i])
|
||||
}
|
||||
bottomBoard.currentHits.length = 0
|
||||
}
|
||||
bot.current = bot.specialHits.pop()
|
||||
bottomBoard.currentHits.push(bot.current)
|
||||
bot.special = bot.back = bot.prev_hit = false
|
||||
bot.first_hit = bot.hunting = true
|
||||
bot.battleLogic()
|
||||
}
|
||||
},
|
||||
|
||||
getNewCurrent: function (direction) {
|
||||
var difference = bottomBoard.currentHits.length - bot.sizeOfShipSunk
|
||||
if (bot.direction == "up") {
|
||||
bot.direction = "down"
|
||||
if (difference > 1) {
|
||||
bot.current += 10 * (bottomBoard.currentHits.length - 1)
|
||||
var temp = bot.current + (10 * (difference - 1))
|
||||
bottomBoard.currentHits.length = 0
|
||||
for (var i = 0; i < difference; i++) {
|
||||
bottomBoard.currentHits.push(temp)
|
||||
temp += 10
|
||||
}
|
||||
bot.case2 = true
|
||||
return false
|
||||
}
|
||||
bot.current += 10 * bot.sizeOfShipSunk
|
||||
return true
|
||||
}
|
||||
if (bot.direction == "right") {
|
||||
bot.direction = "left"
|
||||
if (difference > 1) {
|
||||
bot.current -= bottomBoard.currentHits.length - 1
|
||||
var temp = bot.current + (difference - 1)
|
||||
bottomBoard.currentHits.length = 0
|
||||
for (var i = 0; i < difference; i++) {
|
||||
bottomBoard.currentHits.push(temp)
|
||||
temp -= 1
|
||||
}
|
||||
bot.case2 = true
|
||||
return false
|
||||
}
|
||||
bot.current -= bot.sizeOfShipSunk
|
||||
return true
|
||||
}
|
||||
if (bot.direction == "down") {
|
||||
bot.direction = "up"
|
||||
if (difference > 1) {
|
||||
bot.current -= 10 * (bottomBoard.currentHits.length - 1)
|
||||
var temp = bot.current - (10 * (difference - 1))
|
||||
bottomBoard.currentHits.length = 0
|
||||
for (var i = 0; i < difference; i++) {
|
||||
bottomBoard.currentHits.push(temp)
|
||||
temp -= 10
|
||||
}
|
||||
bot.case2 = true
|
||||
return false
|
||||
}
|
||||
bot.current -= 10 * bot.sizeOfShipSunk
|
||||
return true
|
||||
}
|
||||
if (bot.direction == "left") {
|
||||
bot.direction = "right"
|
||||
if (difference > 1) {
|
||||
bot.current += bottomBoard.currentHits.length - 1
|
||||
var temp = bot.current - (difference - 1)
|
||||
bottomBoard.currentHits.length = 0
|
||||
for (var i = 0; i < difference; i++) {
|
||||
bottomBoard.currentHits.push(temp)
|
||||
temp += 1
|
||||
}
|
||||
bot.case2 = true
|
||||
return false
|
||||
}
|
||||
bot.current += bot.sizeOfShipSunk
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the games grids and layout
|
||||
$(document).ready(function () {
|
||||
for (var i = 1; i <= 100; i++) {
|
||||
// The number and letter designators
|
||||
if (i < 11) {
|
||||
$(".top").prepend("<span class='aTops'>" + Math.abs(i - 11) + "</span>")
|
||||
$(".bottom").prepend("<span class='aTops'>" + Math.abs(i - 11) + "</span>")
|
||||
$(".grid").append("<li class='points offset1 " + i + "'><span class='hole'></span></li>")
|
||||
} else {
|
||||
$(".grid").append("<li class='points offset2 " + i + "'><span class='hole'></span></li>")
|
||||
}
|
||||
if (i == 11) {
|
||||
$(".top").prepend("<span class='aTops hidezero'>" + Math.abs(i - 11) + "</span>")
|
||||
$(".bottom").prepend("<span class='aTops hidezero'>" + Math.abs(i - 11) + "</span>")
|
||||
}
|
||||
if (i > 90) {
|
||||
$(".top").append("<span class='aLeft'>" +
|
||||
String.fromCharCode(97 + (i - 91)).toUpperCase() + "</span>")
|
||||
$(".bottom").append("<span class='aLeft'>" +
|
||||
String.fromCharCode(97 + (i - 91)).toUpperCase() + "</span>")
|
||||
}
|
||||
}
|
||||
$(".text").text(output.welcome)
|
||||
})
|
||||
|
||||
// Start the game setup
|
||||
$(document).ready(function () {
|
||||
$(".one").on("click", function () {
|
||||
$(".text").text(output.player1)
|
||||
gameSetup(this)
|
||||
})
|
||||
$(".multi").on("click", function (e) {
|
||||
e.preventDefault()
|
||||
if (!$("div").hasClass("error")) {
|
||||
$(".text").text(output.not)
|
||||
$(this).addClass("error")
|
||||
}
|
||||
})
|
||||
$(".options").on("click", function (e) {
|
||||
e.preventDefault()
|
||||
if (!$("div").hasClass("error")) {
|
||||
$(".text").text(output.not)
|
||||
$(this).addClass("error")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function gameSetup(t) {
|
||||
$(t).off() && $(".two").off()
|
||||
$(".one").addClass("self").removeClass("one").text("Place My Own")
|
||||
$(".multi").addClass("random").removeClass("multi").text("Random")
|
||||
|
||||
$(".self").off("click").on("click", function () {
|
||||
$(".text").text(output.self)
|
||||
selfSetup(playerFleet)
|
||||
})
|
||||
$(".random").off("click").on("click", function () {
|
||||
playerFleet = new Fleet("Player 1")
|
||||
playerFleet.initShips()
|
||||
randomSetup(playerFleet)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function selfSetup() {
|
||||
$(".self").addClass("horz").removeClass("self").text("Horizontal")
|
||||
$(".random").addClass("vert").removeClass("random").text("Vertical")
|
||||
|
||||
// initialize the fleet
|
||||
playerFleet = new Fleet("Player 1")
|
||||
playerFleet.initShips()
|
||||
// light up the players ship board for placement
|
||||
placeShip(playerFleet.ships[playerFleet.currentShip], playerFleet)
|
||||
}
|
||||
|
||||
function randomSetup(fleet) {
|
||||
// Decide if the ship will be placed vertically or horizontally
|
||||
// if 0 then ship will be places horizontally if 1 vertically
|
||||
// setShip(location, ship, "vert", fleet, "self");
|
||||
if (fleet.currentShip >= fleet.numOfShips) return // regard against undefined length
|
||||
|
||||
var orien = Math.floor((Math.random() * 10) + 1)
|
||||
var length = fleet.ships[fleet.currentShip].length
|
||||
|
||||
if (orien < 6) {
|
||||
// create a random number betwee 1 and 6
|
||||
var shipOffset = 11 - fleet.ships[fleet.currentShip].length
|
||||
var horiz = Math.floor((Math.random() * shipOffset) + 1)
|
||||
var vert = Math.floor(Math.random() * 9)
|
||||
var randNum = parseInt(String(vert) + String(horiz))
|
||||
if (fleet == cpuFleet) checkOverlap(randNum, length, "horz", fleet)
|
||||
else setShip(randNum, fleet.ships[fleet.currentShip], "horz", fleet, "random")
|
||||
} else {
|
||||
var shipOffset = 110 - (fleet.ships[fleet.currentShip].length * 10)
|
||||
var randNum = Math.floor((Math.random() * shipOffset) + 1)
|
||||
|
||||
if (fleet == cpuFleet) checkOverlap(randNum, length, "vert", fleet)
|
||||
else setShip(randNum, fleet.ships[fleet.currentShip], "vert", fleet, "random")
|
||||
}
|
||||
}
|
||||
|
||||
function createCpuFleet() {
|
||||
// create a random ship placement for the cpu's fleet
|
||||
cpuFleet = new Fleet("CPU")
|
||||
cpuFleet.initShips()
|
||||
randomSetup(cpuFleet)
|
||||
}
|
||||
|
||||
|
||||
function placeShip(ship, fleet) {
|
||||
// check orientation of ship and highlight accordingly
|
||||
var orientation = "horz"
|
||||
$(".vert").off("click").on("click", function () {
|
||||
orientation = "vert"
|
||||
})
|
||||
$(".horz").off("click").on("click", function () {
|
||||
orientation = "horz"
|
||||
})
|
||||
// when the user enters the grid have the ships lenght highlighted with the
|
||||
// ships length.
|
||||
$(".bottom").find(".points").off("mouseenter").on("mouseenter", function () {
|
||||
var num = $(this).attr('class').slice(15)
|
||||
//
|
||||
if (orientation == "horz") displayShipHorz(parseInt(num), ship, this, fleet)
|
||||
else displayShipVert(parseInt(num), ship, this, fleet)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function displayShipHorz(location, ship, point, fleet) {
|
||||
var endPoint = location + ship.length - 2
|
||||
if (!(endPoint % 10 >= 0 && endPoint % 10 < ship.length - 1)) {
|
||||
for (var i = location; i < (location + ship.length); i++) {
|
||||
$(".bottom ." + i).addClass("highlight")
|
||||
}
|
||||
$(point).off("click").on("click", function () {
|
||||
setShip(location, ship, "horz", fleet, "self")
|
||||
})
|
||||
}
|
||||
$(point).off("mouseleave").on("mouseleave", function () {
|
||||
removeShipHorz(location, ship.length)
|
||||
})
|
||||
}
|
||||
|
||||
function displayShipVert(location, ship, point, fleet) {
|
||||
var endPoint = (ship.length * 10) - 10
|
||||
var inc = 0
|
||||
if (location + endPoint <= 100) {
|
||||
for (var i = location; i < (location + ship.length); i++) {
|
||||
$(".bottom ." + (location + inc)).addClass("highlight")
|
||||
inc = inc + 10
|
||||
}
|
||||
$(point).off("click").on("click", function () {
|
||||
setShip(location, ship, "vert", fleet, "self")
|
||||
})
|
||||
}
|
||||
$(point).off("mouseleave").on("mouseleave", function () {
|
||||
removeShipVert(location, ship.length)
|
||||
})
|
||||
}
|
||||
|
||||
function removeShipHorz(location, length) {
|
||||
for (var i = location; i < location + length; i++) {
|
||||
$(".bottom ." + i).removeClass("highlight")
|
||||
}
|
||||
}
|
||||
|
||||
function removeShipVert(location, length) {
|
||||
var inc = 0
|
||||
for (var i = location; i < location + length; i++) {
|
||||
$(".bottom ." + (location + inc)).removeClass("highlight")
|
||||
inc = inc + 10
|
||||
}
|
||||
}
|
||||
|
||||
function setShip(location, ship, orientation, genericFleet, type) {
|
||||
if (!(checkOverlap(location, ship.length, orientation, genericFleet))) {
|
||||
if (orientation == "horz") {
|
||||
genericFleet.ships[genericFleet.currentShip].populateHorzHits(location)
|
||||
$(".text").text(output.placed(genericFleet.ships[genericFleet.currentShip].name + " has"))
|
||||
for (var i = location; i < (location + ship.length); i++) {
|
||||
$(".bottom ." + i).addClass(genericFleet.ships[genericFleet.currentShip].name)
|
||||
$(".bottom ." + i).children().removeClass("hole")
|
||||
}
|
||||
if (++genericFleet.currentShip == genericFleet.numOfShips) {
|
||||
$(".text").text(output.placed("ships have"))
|
||||
$(".bottom").find(".points").off("mouseenter")
|
||||
// clear the call stack
|
||||
setTimeout(createCpuFleet, 100)
|
||||
} else {
|
||||
if (type == "random") randomSetup(genericFleet)
|
||||
else placeShip(genericFleet.ships[genericFleet.currentShip], genericFleet)
|
||||
}
|
||||
|
||||
} else {
|
||||
var inc = 0
|
||||
genericFleet.ships[genericFleet.currentShip].populateVertHits(location)
|
||||
$(".text").text(output.placed(genericFleet.ships[genericFleet.currentShip].name + " has"))
|
||||
for (var i = location; i < (location + ship.length); i++) {
|
||||
$(".bottom ." + (location + inc)).addClass(genericFleet.ships[genericFleet.currentShip].name)
|
||||
$(".bottom ." + (location + inc)).children().removeClass("hole")
|
||||
inc = inc + 10
|
||||
}
|
||||
if (++genericFleet.currentShip == genericFleet.numOfShips) {
|
||||
$(".text").text(output.placed("ships have"))
|
||||
$(".bottom").find(".points").off("mouseenter")
|
||||
// clear the call stack
|
||||
setTimeout(createCpuFleet, 100)
|
||||
} else {
|
||||
if (type == "random") randomSetup(genericFleet)
|
||||
else placeShip(genericFleet.ships[genericFleet.currentShip], genericFleet)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (type == "random") randomSetup(genericFleet)
|
||||
else $(".text").text(output.overlap)
|
||||
}
|
||||
} // end of setShip()
|
||||
|
||||
function checkOverlap(location, length, orientation, genFleet) {
|
||||
var loc = location
|
||||
if (orientation == "horz") {
|
||||
var end = location + length
|
||||
for (; location < end; location++) {
|
||||
for (var i = 0; i < genFleet.currentShip; i++) {
|
||||
if (genFleet.ships[i].checkLocation(location)) {
|
||||
if (genFleet == cpuFleet) randomSetup(genFleet)
|
||||
else return true
|
||||
}
|
||||
} // end of for loop
|
||||
} // end of for loop
|
||||
} else {
|
||||
var end = location + (10 * length)
|
||||
for (; location < end; location += 10) {
|
||||
for (var i = 0; i < genFleet.currentShip; i++) {
|
||||
if (genFleet.ships[i].checkLocation(location)) {
|
||||
if (genFleet == cpuFleet) randomSetup(genFleet)
|
||||
else return true
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end of if/else
|
||||
if (genFleet == cpuFleet && genFleet.currentShip < genFleet.numOfShips) {
|
||||
if (orientation == "horz") genFleet.ships[genFleet.currentShip++].populateHorzHits(loc)
|
||||
else genFleet.ships[genFleet.currentShip++].populateVertHits(loc)
|
||||
if (genFleet.currentShip == genFleet.numOfShips) {
|
||||
// clear the call stack
|
||||
setTimeout(startGame, 500)
|
||||
} else randomSetup(genFleet)
|
||||
}
|
||||
return false
|
||||
} // end of checkOverlap()
|
||||
|
||||
|
||||
function startGame() {
|
||||
$(".layout").fadeOut("fast", function () {
|
||||
$(".console").css({ "margin-top": "31px" })
|
||||
})
|
||||
$(".text").text(output.start)
|
||||
// Generate all possible hits for Player 1
|
||||
for (var i = 0; i < 100; i++) bot.randPool[i] = i + 1
|
||||
highlightBoard()
|
||||
}
|
||||
|
||||
function highlightBoard() {
|
||||
if (playerFleet.ships.length == 0) {
|
||||
$(".top").find(".points").off("mouseenter").off("mouseleave").off("click")
|
||||
} else {
|
||||
$(".top").find(".points").off("mouseenter mouseover").on("mouseenter mouseover", function () {
|
||||
// only allow target highlight on none attempts
|
||||
if (!($(this).hasClass("used"))) topBoard.highlight(this)
|
||||
})
|
||||
}
|
||||
}
|
350
参考项目/bs/style.css
Normal file
@ -0,0 +1,350 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700);
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main {
|
||||
width: 700px;
|
||||
height: 800px;
|
||||
margin: auto;
|
||||
|
||||
}
|
||||
|
||||
.board {
|
||||
position: absolute;
|
||||
width: 380px;
|
||||
height: 740px;
|
||||
margin: 25px auto;
|
||||
border: 2px solid #73B1B7;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.displays {
|
||||
position: relative;
|
||||
margin: 25px;
|
||||
}
|
||||
|
||||
.top {
|
||||
width: 330px;
|
||||
height: 330px;
|
||||
background: #008B8B;
|
||||
border-left: 1px solid #8DEEEE;
|
||||
border-bottom: 1px solid #8DEEEE;
|
||||
|
||||
}
|
||||
|
||||
.bottom {
|
||||
width: 330px;
|
||||
height: 330px;
|
||||
margin-top: 25px;
|
||||
background: #008B8B;
|
||||
border-left: 1px solid #8DEEEE;
|
||||
border-bottom: 1px solid #8DEEEE;
|
||||
}
|
||||
|
||||
.grid {
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
margin-left: 30px;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.aTops {
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
border-top: 1px solid #8DEEEE;
|
||||
border-right: 1px solid #8DEEEE;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.aLeft {
|
||||
position: relative;
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
border-top: 1px solid #8DEEEE;
|
||||
border-right: 1px solid #8DEEEE;
|
||||
display: block;
|
||||
line-height: 29px;
|
||||
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.points {
|
||||
position: relative;
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
border-top: 1px solid #8DEEEE;
|
||||
border-right: 1px solid #8DEEEE;
|
||||
display: inline-block;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background: rgba(144, 144, 144, 0.9);
|
||||
}
|
||||
|
||||
.hidezero {
|
||||
color: #008B8B;
|
||||
}
|
||||
|
||||
.hole {
|
||||
position: relative;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: 9px;
|
||||
border: 1px solid #8DEEEE;
|
||||
border-radius: 50%;
|
||||
display: table;
|
||||
}
|
||||
|
||||
|
||||
.panel {
|
||||
float: right;
|
||||
width: 275px;
|
||||
height: 740px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.topPanel {
|
||||
width: 275px;
|
||||
height: 350px;
|
||||
border: 2px solid #73B1B7;
|
||||
border-radius: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bottomPanel {}
|
||||
|
||||
.console {
|
||||
position: relative;
|
||||
width: 275px;
|
||||
height: 350px;
|
||||
margin-top: 36px;
|
||||
display: block;
|
||||
|
||||
background: rgba(144, 144, 144, 0.6);
|
||||
border: 2px solid #73B1B7;
|
||||
border-radius: 10px;
|
||||
|
||||
color: green;
|
||||
}
|
||||
|
||||
.text {
|
||||
position: relative;
|
||||
width: 250px;
|
||||
height: 375px;
|
||||
margin: 12px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
|
||||
margin: 50px auto;
|
||||
border: 2px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.buttons:hover {
|
||||
background: rgba(169, 169, 169, 0.3);
|
||||
}
|
||||
|
||||
.target {
|
||||
background: rgba(250, 10, 10, 0.7);
|
||||
}
|
||||
|
||||
.carrier {
|
||||
background: rgba(36, 36, 36, 1);
|
||||
}
|
||||
|
||||
.battleship {
|
||||
background: rgba(49, 49, 49, 0.9);
|
||||
}
|
||||
|
||||
.cruiser {
|
||||
background: rgba(64, 64, 64, 0.9);
|
||||
}
|
||||
|
||||
.destroyer {
|
||||
background: rgba(81, 81, 81, 0.7);
|
||||
}
|
||||
|
||||
.frigate {
|
||||
background: rgba(100, 100, 100, 0.7);
|
||||
}
|
||||
|
||||
.one {}
|
||||
|
||||
.self {}
|
||||
|
||||
.random {}
|
||||
|
||||
.error {}
|
||||
|
||||
.turn {}
|
||||
|
||||
.hit {
|
||||
position: relative;
|
||||
margin: 1.5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
|
||||
|
||||
background: rgb(220, 10, 10);
|
||||
border: 1px solid red;
|
||||
border-radius: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
display: table;
|
||||
|
||||
animation-name: expand;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: none;
|
||||
|
||||
-webkit-animation-name: expand;
|
||||
-webkit-animation-duration: 0.5s;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-fill-mode: none;
|
||||
|
||||
-moz-animation-name: expand;
|
||||
-moz-animation-duration: 0.5s;
|
||||
-moz-animation-iteration-count: 1;
|
||||
-moz-animation-timing-function: linear;
|
||||
-moz-animation-fill-mode: none;
|
||||
}
|
||||
|
||||
.miss {
|
||||
position: relative;
|
||||
margin: 1.5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
|
||||
background: rgb(250, 250, 250);
|
||||
border: 1px solid white;
|
||||
border-radius: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
display: table;
|
||||
|
||||
animation: expand;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: none;
|
||||
|
||||
-webkit-animation: expand;
|
||||
-webkit-animation-duration: 0.5s;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-fill-mode: none;
|
||||
|
||||
-moz-animation: expand;
|
||||
-moz-animation-duration: 0.5s;
|
||||
-moz-animation-iteration-count: 1;
|
||||
-moz-animation-timing-function: linear;
|
||||
-moz-animation-fill-mode: none;
|
||||
|
||||
}
|
||||
|
||||
@keyframes expand {
|
||||
0% {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
top: 12.5px;
|
||||
left: 12.5px;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes expand {
|
||||
0% {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
top: 12.5px;
|
||||
left: 12.5px;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes expand {
|
||||
0% {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
top: 12.5px;
|
||||
left: 12.5px;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
}
|
BIN
参考项目/military-boats-collection_1284-37419.jpg
Normal file
After Width: | Height: | Size: 80 KiB |