This commit is contained in:
赵鑫 2022-08-12 22:08:31 +08:00
parent a246dddc0e
commit da025636c7
95 changed files with 6372 additions and 2 deletions

120
.gitignore vendored Normal file
View File

@ -0,0 +1,120 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -1,3 +1,8 @@
# battleship
# 海战 Battleship
海战
## 参考
- [海战 (游戏)-维基百科](https://zh.m.wikipedia.org/wiki/%E6%B5%B7%E6%88%98_(%E6%B8%B8%E6%88%8F))
- [Battleship-维基百科](https://en.wikipedia.org/wiki/Battleship_(game))
- [一个单人游戏的例子](https://billmei.github.io/battleboat/)
- [一个多人游戏的例子](http://zh.battleship-game.org/)

View File

@ -0,0 +1,3 @@
*.xlsx
*.xls
*.csv

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Bill Mei
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View 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.

View File

@ -0,0 +1,319 @@
@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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,95 @@
<!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="description" content="Battleboat.js: A JavaScript AI that beats humans at battleship.">
<meta name="keywords" content="Battleboat.js, battleship, AI, robot, JavaScript">
<meta name="google" content="notranslate">
<link rel="icon" type="image/png" href="img/favicon.png" />
<link rel="image_src" href="img/apple-touch-icon-144x144-precomposed.png" />
<link rel="apple-touch-icon" href="img/apple-touch-icon.png" />
<link rel="apple-touch-icon-precomposed" href="img/apple-touch-icon-precomposed.png" />
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="img/apple-touch-icon-57x57-precomposed.png" />
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="img/apple-touch-icon-72x72-precomposed.png" />
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="img/apple-touch-icon-114x114-precomposed.png" />
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="img/apple-touch-icon-144x144-precomposed.png" />
<link rel="author" href="https://plus.google.com/+BillMei" />
<link rel="publisher" href="https://plus.google.com/+BillMei" />
<link href="css/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>
<p>Created by <a href="https://billmei.net">Bill Mei</a>.</p>
<p>This project is open source <a href="https://github.com/billmei/battleboat">on GitHub</a>.</p>
</div>
<a href="https://github.com/billmei/battleboat"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
<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="js/battleboat.js"></script>
<span class="prefetch" id="prefetch1"></span>
<span class="prefetch" id="prefetch2"></span>
<span class="prefetch" id="prefetch3"></span>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
}
body,
html {
width: 1260px;
margin: auto;
color: darkslategray;
background-color: lightgrey;
font-family: Georgia, 'Times New Roman', Times, serif;
}
h1,
h2 {
text-align: center;
margin: 2rem 0 0.5rem;
text-shadow: 1px 1px 2px white;
}
#游戏区域 {
display: flex;
justify-content: space-between;
}
.棋盘 {
width: 600px;
height: 600px;
display: flex;
flex-wrap: wrap;
position: relative;
}
#我方海域 {
background: url(images/sea.png);
}
#敌方海域 {
background: url(images/grey.png);
cursor: url(images/crosshair.png) 16 16, crosshair;
}
.战舰 {
cursor: pointer;
display: flex;
flex-wrap: wrap;
position: absolute;
left: 60px;
}
.甲板 {
width: 60px;
height: 60px;
}
#航空母舰 {
width: 60px;
height: 300px;
top: 60px;
}
#航空母舰.水平放置 {
height: 60px;
width: 300px;
}
#航空母舰0.甲板 {
background: url(images/ship1.png) -2px 0px no-repeat;
}
#航空母舰1.甲板 {
background: url(images/ship1.png) -2px -60px no-repeat;
}
#航空母舰2.甲板 {
background: url(images/ship1.png) -2px -120px no-repeat;
}
#航空母舰3.甲板 {
background: url(images/ship1.png) -2px -180px no-repeat;
}
#航空母舰4.甲板 {
background: url(images/ship1.png) -2px -240px no-repeat;
}
#战列舰 {
width: 60px;
height: 240px;
top: 120px;
}
#战列舰.水平放置 {
width: 240px;
height: 60px;
}
#战列舰0.甲板 {
background: url(images/ship2.png) -2px 0px no-repeat;
}
#战列舰1.甲板 {
background: url(images/ship2.png) -2px -60px no-repeat;
}
#战列舰2.甲板 {
background: url(images/ship2.png) -2px -120px no-repeat;
}
#战列舰3.甲板 {
background: url(images/ship2.png) -2px -180px no-repeat;
}
#巡洋舰 {
width: 60px;
height: 180px;
top: 180px;
}
#驱逐舰 {
width: 60px;
height: 180px;
top: 240px;
}
#驱逐舰.水平放置,
#巡洋舰.水平放置 {
width: 180px;
height: 60px;
}
#巡洋舰0.甲板 {
background: url(images/ship3.png) -2px 0px no-repeat;
}
#巡洋舰1.甲板 {
background: url(images/ship3.png) -2px -60px no-repeat;
}
#巡洋舰2.甲板 {
background: url(images/ship3.png) -2px -120px no-repeat;
}
#驱逐舰0.甲板 {
background: url(images/ship4.png) -2px 0px no-repeat;
}
#驱逐舰1.甲板 {
background: url(images/ship4.png) -2px -60px no-repeat;
}
#驱逐舰2.甲板 {
background: url(images/ship4.png) -2px -120px no-repeat;
}
#巡逻艇 {
width: 60px;
height: 120px;
top: 300px;
}
#巡逻艇.水平放置 {
width: 120px;
height: 60px;
}
#巡逻艇0.甲板 {
background: url(images/ship5.png) -2px 0px no-repeat;
}
#巡逻艇1.甲板 {
background: url(images/ship5.png) -2px -60px no-repeat;
}
.水平放置 .甲板 {
transform-origin: top left;
transform: scaleY(-1) rotate(-90deg);
}
.拖拽中 {
z-index: -10;
}
.平静海面 {
width: 60px;
height: 60px;
position: absolute;
background: url(images/sea.png) no-repeat center;
}
.爆炸海面 {
width: 60px;
height: 60px;
position: absolute;
background: url(images/boom.png) no-repeat center, url(images/sea.png) no-repeat center;
}

View File

@ -0,0 +1,101 @@
let 游戏已开始 = false
const 我方舰队 = document.querySelectorAll('.战舰')
我方舰队.forEach((ship) => {
// 拖拽开始记录拖拽位置
ship.addEventListener('dragstart', (event) => {
ship.classList.add('拖拽中')
const rect = ship.getBoundingClientRect()
ship.dataset.drag_left = event.clientX - rect.left
ship.dataset.drag_top = event.clientY - rect.top
event.dataTransfer.setData('mouse_x', event.layerX)
event.dataTransfer.setData('mouse_y', event.layerY)
// DEBUG
console.log(`拖动了${ship.id},拖动点位于船上(${ship.dataset.drag_left},${ship.dataset.drag_top})`)
})
// 拖拽结束更新战舰位置
ship.addEventListener('dragend', (event) => {
// const 其它战舰 = [...我方海域.querySelectorAll('.战舰:not(.拖拽中)')]
// event.dataTransfer.setData('a', event.clientX)
// event.dataTransfer.setData('b', event.clientY)
let mouse_x = event.dataTransfer.getData('mouse_x')
let mouse_y = event.dataTransfer.getData('mouse_y')
let x = event.layerX
let y = event.layerY
console.log(parseInt(Math.ceil((x - mouse_x) / 60)), parseInt(Math.ceil((y - mouse_y) / 60)))
// 判断移动是否合法
// 更新战舰显示位置
ship.style.left = `${ship.dataset.drop_left}px`
ship.style.top = `${ship.dataset.drop_top}px`
ship.classList.remove('拖拽中')
delete ship.dataset.drag_left
delete ship.dataset.drag_top
delete ship.dataset.drop_left
delete ship.dataset.drop_top
// DEBUG
// console.log(我方海域状态)
})
// 左键点击旋转战舰
ship.addEventListener('click', (event) => {
// TODO: 检查旋转是否合法1游戏未开始2没有碰撞其它战舰3没有出边界
if (游戏已开始) return
ship.classList.toggle('水平放置')
// DEGUB
console.log(`点击了[ ${ship.id} ]的[ ${event.target.id} ]甲板`)
})
})
我方海域.addEventListener('dragover', (event) => {
event.preventDefault()
const dragging_ship = document.querySelector('.拖拽中')
if (event.target === 我方海域) {
const 拖拽中的战舰 = document.querySelector('.拖拽中')
拖拽中的战舰.dataset.drop_left = parseInt(Math.ceil(event.layerX - 拖拽中的战舰.dataset.drag_left + 30) / 60) * 60
拖拽中的战舰.dataset.drop_top = parseInt(Math.ceil(event.layerY - 拖拽中的战舰.dataset.drag_top + 30) / 60) * 60
// DEBUG
console.log(`拖拽到了(${拖拽中的战舰.dataset.drop_left}, ${拖拽中的战舰.dataset.drop_top})`)
}
// const 拖拽中的战舰 = document.querySelector('.拖拽中')
// const other_ships = [...我方海域.querySelectorAll('.ship:not(.拖拽中)')]
// let leading_ship = null
// for (let ship of other_ships) {
// const ship_rect = ship.getBoundingClientRect()
// const mouse_offset = mouse_x - ship_rect.left - ship_rect.width / 2
// if (mouse_offset > 0) leading_ship = ship
// }
// if (leading_ship) leading_ship.after(拖拽中的战舰)
// else 我方海域.prepend(拖拽中的战舰)
})
我方海域.addEventListener('dragend', (event) => {
// console.log(event)
})
敌方海域.addEventListener('click', (event) => {
if (event.target !== 敌方海域) return
// 展开迷雾
const area = document.createElement('div')
if (Math.random() > 0.5) area.classList.add('平静海面')
else area.classList.add('爆炸海面')
const col = parseInt(event.layerX / 60)
const row = parseInt(event.layerY / 60)
area.style.left = `${col * 60}px`
area.style.top = `${row * 60}px`
敌方海域.append(area)
// DEBUG
console.log(`炮击地方海域(${col}, ${row})`)
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="battleship.css" />
<title>海战 Battleship</title>
</head>
<body>
<h1>海战 Battleship</h1>
<div id="游戏区域">
<div id="我方">
<h2>我方海域 Player Area</h2>
<div id="我方海域" class="棋盘">
<div id="航空母舰" class="战舰 水平放置" draggable="true" title="航空母舰 Carrier">
<div id="航空母舰0" class="甲板" data-location="11"></div>
<div id="航空母舰1" class="甲板" data-location="21"></div>
<div id="航空母舰2" class="甲板" data-location="31"></div>
<div id="航空母舰3" class="甲板" data-location="41"></div>
<div id="航空母舰4" class="甲板" data-location="51"></div>
</div>
<div id="战列舰" class="战舰 水平放置" draggable="true" title="战列舰 Battleship">
<div id="战列舰0" class="甲板" data-location="12"></div>
<div id="战列舰1" class="甲板" data-location="22"></div>
<div id="战列舰2" class="甲板" data-location="32"></div>
<div id="战列舰3" class="甲板" data-location="42"></div>
</div>
<div id="巡洋舰" class="战舰 水平放置" draggable="true" title="巡洋舰 Cruiser">
<div id="巡洋舰0" class="甲板" data-location="13"></div>
<div id="巡洋舰1" class="甲板" data-location="23"></div>
<div id="巡洋舰2" class="甲板" data-location="33"></div>
</div>
<div id="驱逐舰" class="战舰 水平放置 沉没" draggable="true" title="驱逐舰 Destroyer">
<div id="驱逐舰0" class="甲板" data-location="14"></div>
<div id="驱逐舰1" class="甲板" data-location="24"></div>
<div id="驱逐舰2" class="甲板" data-location="34"></div>
</div>
<div id="巡逻艇" class="战舰 水平放置" draggable="true" title="巡逻艇 Patrol Boat">
<div id="巡逻艇0" class="甲板" data-location="15"></div>
<div id="巡逻艇1" class="甲板" data-location="25"></div>
</div>
</div>
</div>
<div id="敌方">
<h2>敌方海域 Enemy Area</h2>
<div id="敌方海域" class="棋盘"></div>
</div>
</div>
</div>
<script src="battleship.js" defer></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
{
"name": "battleship.js",
"version": "0.1.0",
"description": "A multiplayer battleship game.",
"main": "server.js",
"scripts": {
"start": "node server",
"dev": "nodemon server"
},
"keywords": [
"multiplayer",
"battleship",
"game"
],
"author": "Zhao Xin <7176466@qq.com>",
"license": "MIT",
"devDependencies": {
"nodemon": "^2.0.15"
},
"dependencies": {
"express": "^4.17.3",
"socket.io": "^4.4.1"
}
}

View File

@ -0,0 +1,41 @@
const client = io()
client.on('connect', () => {
console.log('connected')
})
client.on('welcome', () => {
console.log('welcome')
})
const CARRIER = 1 // 航空母舰 5
const BATTLESHIP = 2 // 战列舰 4
const DESTROYER = 3 // 驱逐舰 3
const SUBMARINE = 4 // 潜艇 3
const PATROLBOAT = 5 // 巡逻艇 2
CONST.EMPTY = 0
CONST.SHIP = 1
CONST.MISS = 2
CONST.HIT = 3
CONST.SUNK = 4
function player_click(event) {
const x = event.target.dataset.x
const y = event.target.dataset.y
const player = event.target.dataset.player
const status = event.target.dataset.status
console.log(x, y, player, status)
}
function enemy_click(event) {
const x = event.target.dataset.x
const y = event.target.dataset.y
const player = event.target.dataset.player
const status = event.target.dataset.status
console.log(x, y, player, status)
}
function ship_rotate(event) {
event.target.classList.toggle('ship-horizontal')
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,284 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>海战 Battleship</title>
</head>
<body>
<h1>海战 Battleship</h1>
<div id="game">
<div class="row">
<table id="player1" class="gameboard">
<thead>
<tr>
<th>我方舰队 Your Fleet</th>
</tr>
</thead>
<tbody>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="0" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="0" onclick="player_click(event)"></td>
<td class="cell ship11" data-player="0" data-status="0" data-y="1" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="1" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="0" onclick="player_click(event)"></td>
<td class="cell ship12h" data-player="0" data-status="0" data-y="2" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="2" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="3" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="4" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="5" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="6" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="7" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="8" data-x="9" onclick="player_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="0" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="1" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="2" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="3" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="4" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="5" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="6" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="7" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="8" onclick="player_click(event)"></td>
<td class="cell" data-player="0" data-status="0" data-y="9" data-x="9" onclick="player_click(event)"></td>
</tr>
</tbody>
</table>
<table id="player2" class="gameboard">
<thead>
<tr>
<th>敌方舰队 Enemy Fleet</th>
</tr>
</thead>
<tbody>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="0" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="1" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="2" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="3" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="4" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="5" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="6" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="7" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="8" data-x="9" onclick="enemy_click(event)"></td>
</tr>
<tr>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="0" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="1" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="2" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="3" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="4" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="5" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="6" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="7" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="8" onclick="enemy_click(event)"></td>
<td class="cell" data-player="1" data-status="0" data-y="9" data-x="9" onclick="enemy_click(event)"></td>
</tr>
</tbody>
</table>
</div>
<div id="ships" class="row">
<div class="ship ship-carrier" id="carrier" data-size="5" onclick="ship_rotate(event)" title="航空母舰 Carrier"></div>
<div class="ship ship-battleship" id="battleship" data-size="4" onclick="ship_rotate(event)" title="战列舰 Battleship"></div>
<div class="ship ship-destroyer" id="destroyer" data-size="3" onclick="ship_rotate(event)" title="战列舰 Battleship"></div>
<div class="ship ship-submarine" id="submarine" data-size="3" onclick="ship_rotate(event)" title="潜艇 Submarine"></div>
<div class="ship ship-patrolboat" id="patrolboat" data-size="2" onclick="ship_rotate(event)" title="护卫舰 Patrol Boat"></span>
</div>
</div>
<script src="/socket.io/socket.io.min.js"></script>
<script src="battleship.js"></script>
</body>
</html>

View File

@ -0,0 +1,174 @@
/* @import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); */
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
}
html,
body {
overflow-x: hidden;
background-color: #ddd;
}
h1 {
text-align: center;
margin: 40px auto;
color: darkblue;
}
.row {
width: 1280px;
margin: auto;
display: flex;
justify-content: space-between;
border: 1px solid red;
}
.gameboard {
width: 600px;
height: 600px;
padding: 5px;
border-radius: 5px;
background-color: darkblue;
color: aquamarine;
}
#player1 tbody {
cursor: pointer;
}
#player2 tbody {
cursor: url(images/crosshair.png) 16 16, crosshair;
}
.gameboard tbody tr {
display: flex;
}
.cell {
width: 58px;
height: 58px;
margin: 1px;
background: url(images/tile.png) no-repeat center;
border-radius: 3px;
z-index: 0;
position: relative;
overflow: visible;
}
.ship11 {
background: url(images/ship1.png) -2px 0px no-repeat, url(images/tile.png) no-repeat center;
}
.ship12 {
background: url(images/ship1.png) -2px -60px no-repeat, url(images/tile.png) no-repeat center;
}
.ship12h {
background: url(images/boom60.png) 0px 0px no-repeat, url(images/ship1.png) -2px -60px no-repeat, url(images/tile.png) no-repeat center;
}
#ships {
display: flex;
justify-content: left;
}
.ship {
width: 60px;
border: 1px solid red;
}
.ship-carrier {
height: 300px;
background: url(images/ship1.png) -2px -1px no-repeat;
}
.ship-battleship {
height: 240px;
background: url(images/ship2.png) -2px -1px no-repeat;
}
.ship-destroyer {
height: 180px;
background: url(images/ship3.png) -2px -1px no-repeat;
}
.ship-submarine {
height: 180px;
background: url(images/ship4.png) -2px -1px no-repeat;
}
.ship-patrolboat {
height: 120px;
background: url(images/ship5.png) -2px -1px no-repeat;
}
.ship-carrier.ship-horizontal {
width: 300px;
}
.ship-battleship.ship-horizontal {
width: 240px;
}
.ship-destroyer.ship-horizontal,
.ship-submarine.ship-horizontal {
width: 180px;
}
.ship-patrolboat.ship-horizontal {
width: 120px;
}
.ship-horizontal {
width: 60px !important;
transform-origin: top left;
transform: rotate(-90deg);
margin-top: 60px;
}
/* .ship1 {
z-index: 1000;
}
.ship1::after {
content: '';
z-index: 1000;
position: absolute;
top: 0;
left: 0;
background-position: bottom;
background-size: cover;
width: 60px;
height: 300px;
background: url(images/ship1.png) -2px 0px no-repeat;
transform-origin: top left;
transform: scaleY(-1) rotate(-90deg);
} */
.gameboard tbody td:hover {
/* border: 2px dashed aqua; */
}
/* .container {
width: 100vw;
background-color: orange;
margin: 10px auto;
padding: 10px;
display: flex;
justify-content: space-evenly;
}
.gameboard {
width: 600px;
height: 600px;
margin: 10px;
background-color: aqua;
}
.ship {
width: 100px;
height: 20px;
background-color: blue;
border: 1px black solid;
} */

View File

@ -0,0 +1,11 @@
const express = require('express')
const app = express()
app.use(express.static(require('path').join(__dirname, 'public')))
const host = process.env.HOST || 'localhost'
const port = process.env.PORT || 3000
const server = require('http').createServer(app)
const io = require('socket.io')(server)
io.on('connection', (client) => {
client.emit('welcome')
})
server.listen(port, host, () => console.log(`battleship game server listening at http://${host}:${port}/`))

@ -0,0 +1 @@
Subproject commit 05bbc36a563ee4ce2ab0b111450f77f2a3b6539a

@ -0,0 +1 @@
Subproject commit ed4a739219b343f6f6ae1781d5ea36beb58faea7