js实现可自己编写新机器人的机器人大战游戏代码

代码语言:html

所属分类:游戏

代码描述:js实现可自己编写新机器人的机器人大战游戏代码,内置了5款机器人,你可以直接写新的机器人加入对战,玩法:两个机器人进入竞技场。获胜的机器人将获得荣耀和财富。 移动 每个机器人都有一个“大脑”类,可以被赋予逻辑,以便在游戏每次更新时做出决定。(默认更新速率为 100 毫秒。) 机器人每次更新可以做出的可能决定有: “左” “右” “上” “下” “射击” 能力 此外,你创建的大脑将有 5 个可能的点数分配给机器人。它们将在以下能力之间分配: 护甲 扫描距离 子弹威力 你可以给每个能力任意数量的点

代码标签: js 自己 编写 机器人 机器人 大战 游戏 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  

  <meta name="viewport" content="width=device-width, initial-scale=1">
<link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/prism.css">
<style>
body {
	background: #222;
}
#arena {
	border: 1px solid rgba(255, 255, 255, 0.2);
	height: auto;
	width: calc(100% - 2px);
	max-width: 500px;
	margin-top: 3em;
}
button {
	cursor: pointer;
	font-family: "Roboto", sans-serif;
}
.container {
	left: 50%;
	position: absolute;
	transform: translate(-50%);
	width: 100vw;
	max-width: 500px;
}
#log {
	height: 80px;
	width: 100%;
	/* 	max-width: 500px; */
	background: #ccc;
	border-radius: 10px;
	font-family: monospace;
	overflow-y: auto;
	border: 2px dashed #666;
	width: calc(100% - 6px);
}

#log > span {
	display: block;
	padding: 5px;
	border: 1px solid #111;
	border-radius: 10px;
	box-sizing: border-box;
	color: #333;
}
.shield, .bandolier {
	transition: stroke-dasharray 200ms;
}
.bot,
.shield,
.scan,
.bandolier {
	transition: 100ms;
}
.startBtn:hover,
.endBtn:hover {
	opacity: 0.7;
}
.startBtn,
.endBtn {
	border-radius: 5px;
	display: inline-block;
	background: none;
	padding: 5px;
	width: 100px;
	font-weight: bold;
	font-size: 1.8em;
	color: #f5f5f5;
	font-family: monospace;
}
.startBtn {
	float: right;
	border: 2px solid forestgreen;
}
.endBtn {
	float: left;
	border: 2px solid firebrick;
}
.matchupContainer {
	position: relative;
	display: inline-block;
	width: 100%;
	margin-bottom: 5px;
}
.versus {
	color: #f5f5f5;
	font-family: monospace;
	position: absolute;
	left: 50%;
	transform: translate(-50%);
	padding-top: 6px;
}
.s1 {
	float: left;
}
.s2 {
	float: right;
}
.selectedFighter {
	cursor: pointer;
	position: relative;
	border: 1px solid #111;
	border-radius: 10px;
	width: 200px;
	max-width: 45%;
	padding: 4px;
	font-weight: bold;
	background: goldenrod;
	color: #fff;
	/* 	font-family: "Roboto", sans-serif; */
	font-family: monospace;
	font-size: 1.3em;
	text-shadow: 0.5px 0px 2px #000;
	padding-left: 8px;
	padding-right: 8px;
}
.selectedFighter > option {
	cursor: pointer;
	text-shadow: 0.5px 0px 2px #000;
}
option {
	text-align: center;
	text-shadow: 0.5px 0px 2px #000;
	cursor: pointer;
}
.templateLoader {
	background-color: rgba(155, 155, 155, 0.8);
	padding: 5px;
	font-size: 80%;
	width: 172px;
	position: relative;
	left: 50%;
	border-radius: 0 0 5px 5px;
	transform: translate(-50%);
}
/* menu stuff */
.menuArea {
	position: fixed;
	top: 0%;
	right: 0%;
	z-index: 999;
	height: 100%;
	width: 200px;
	border-left: 3px solid rgb(50, 50, 50);
	border-right: 3px solid rgb(50, 50, 50);
	box-sizing: border-box;
	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	display: flex;
	flex-wrap: wrap;
	flex-direction: column;
	background: rgba(60, 60, 60, 0.8);
	opacity: 1;
	transition: 200ms;
	overflow-y: auto;
	overflow-x: hidden;
	box-shadow: -3px 0px 5px rgba(25, 25, 25, 0.5);
	color: #fff;
	font-family: "Roboto", sans-serif;
}
.btnContainer {
	margin-top: 70px;
	margin-bottom: 20px;
	text-align: center;
}
.btnContainer > button {
	flex: 1;
	width: 100%;
	height: 48px;
	cursor: pointer;
	background: rgb(100, 100, 100);
	color: #fff;
	font-size: 1.05em;
	font-family: monospace;
}

.btnContainer > button:hover {
	opacity: 0.8;
}

.leftPlayerBtn {
	height: 3em;
	width: 3em;
	position: fixed;
	left: 10px;
	top: 10px;
	background: rgba(0, 0, 0, 0.8);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24' stroke='%23fff' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round' class='css-i6dzq1'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	border: 2px solid rgb(150, 150, 150);
	border-radius: 50%;
	z-index: 9999;
	box-shadow: 2px 0px 3px #000;
	cursor: pointer;
	transition: 100ms;
}
.rightPlayerBtn {
	height: 3em;
	width: 3em;
	position: fixed;
	right: 10px;
	top: 10px;
	background: #000;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80' width='24px' height='24px'%3E%3Cpath d='M 35 38 L 28 43 L 24 38 L 16 42 L 8 39 L 6 31 L 13 31 L 18 24 L 5 24 L 11 16 L 14 20 L 18 11 L 32 5 L 20 18 L 31 12 L 38 5 L 50 5 L 39 12 L 35 22 L 30 18 L 23 23 L 17 35 L 26 32 L 26 28 L 30 24 L 31 34 L 38 31 L 35 27 L 42 23 L 43 33 L 48 31 L 44 17 L 47 13 L 51 25 L 58 20 L 50 10 L 59 9 L 59 15 L 63 12 L 63 18 L 69 17 L 74 26 L 67 21 L 58 25 L 53 34 L 42 38 L 42 42 L 27 48 L 32 51 L 39 48 L 48 46 L 51 41 L 56 42 L 56 37 L 62 35 L 61 29 L 68 29 L 73 32 L 72 39 L 74 45 L 72 55 L 67 52 L 68 46 L 67 36 L 63 40 L 57 47 L 48 53 L 39 53 L 35 57 L 44 59 L 56 54 L 62 48 L 63 56 L 67 57 L 61 60 L 54 59 L 51 64 L 56 64 L 53 69 L 58 71 L 55 76' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	border: 2px solid rgb(150, 150, 150);
	border-radius: 50%;
	z-index: 9999;
	box-shadow: 2px 0px 3px #000;
	cursor: pointer;
	transition: 100ms;
}
.rightPlayerBtn:hover,
.infoBtn:hover {
	opacity: 0.8;
}
.rightPlayerBtn:active,
.infoBtn:active {
	box-shadow: 1px 0px 1px #000;
}
.closed {
	height: 0%;
	overflow: hidden;
}

.closed > * {
	display: none;
}
.customBrain {
	max-width: 180px;
	min-width: 180px;
	width: 180px;
	min-height: 200px;
}

/* modal stuff */
dialog {
	z-index: 1000;
	background: #ccc;
	box-shadow: 0px 3px 8px #000;
	border-radius: 8px;
	border: none;
	padding-top: 35px;
	position: relative;
	font-family: "Roboto", sans-serif;
	min-width: 350px;
	max-width: 500px;
	width: 80%;
	min-height: 500px;
	max-height: 700px;
	height: 80%;
	overflow: hidden;
}
.gameInfo {
	max-height: 100%;
	width: 100%;
	overflow-y: auto;
}
.gameInfo > h3 {
	text-align: center;
}
.closeDialog {
	position: absolute;
	font-size: 2em;
/* 	font-weight: 900; */
	top: 0px;
	right: 0px;
	border: none;
	background: none;
	cursor: pointer;
	height: 30px;
	width: 30px;
	color: #fff;
	font-family: monospace;
}
.closeDialog:hover {
	color: rgba(0, 0, 0, 0.5);
}
.header {
	text-align: center;
	margin-top: -30px;
}
#open {
	margin-top: 12px;
	width: 100px;
	border: 2px solid rgba(0,255,255,0.5);
	background: none;
	color: #0ff;
	padding: 10px;
	font-family: "Roboto", sans-serif;
	border-radius: 20px;
	cursor: pointer;
	position: absolute;
}

/* width */
::-webkit-scrollbar {
	width: 8px;
}

/* Track */
::-webkit-scrollbar-track {
	box-shadow: inset 0 0 3px grey;
	border-radius: 10px;
}

/* Handle */
.menuArea::-webkit-scrollbar-thumb {
	background: #666;
	border-radius: 10px;
}

/* Handle on hover */
.menuArea::-webkit-scrollbar-thumb:hover {
	background: #888;
}
/* Handle */
.gameInfo::-webkit-scrollbar-thumb {
	background: #666;
	border-radius: 10px;
}

/* Handle on hover */
.gameInfo::-webkit-scrollbar-thumb:hover {
	background: #888;
}
/* Handle */
::-webkit-scrollbar-thumb {
	background: #666;
	border-radius: 10px;
}

/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
	background: #888;
}
.playerLink {
	word-wrap: break-word;
}
.newPlayer {
	border: 2px dotted #fff;
	border-radius: 5px;
	padding: 5px;
}
</style>



  
  
</head>

<body translate="no">
  <button title="Settings" class="rightPlayerBtn"></button>

<div class="menuArea closed">
	<button id="open" title="How the game is played">How to Play</button>
	<dialog>
		<h3 class="header">How to Play</h3>
		<button type="button" class="closeDialog" title="Close">x</button>
		<div class="gameInfo">Just figure it out... Duh 🙄
			<br />
			<br />
			No, but for real, 2 bots enter. 1 bot leaves victorious, shrouded in glory and riches.
			<br />
			<br />
			<hr />
			<h3>Moves</h3>
			Each bot will have a Brain class that can be given logic to make a decision every time the game updates.
			<br />
			<small>(The default update rate is 100ms.)</small>
			<br />
			<br />
			The possible decisions a bot can make each update are
			<ul>
				<li><b>"left"</b></li>
				<li><b>"right"</b></li>
				<li><b>"up"</b></li>
				<li><b>"down"</b></li>
				<li><b>"shoot"</b></li>
			</ul>
			<br />
			<hr />
			<h3>Powers</h3>
			Additionally, your created brain will have <i>5</i> possible points to assign to the bot. They will be split between
			<ul>
				<li><svg viewBox="0 0 24 24" width="16" height="16" stroke="green" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1">
						<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
					</svg> Armor</li>
				<li><svg viewBox="0 0 24 24" width="16" height="16" stroke="steelblue" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1">
						<circle cx="12" cy="12" r="2"></circle>
						<path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path>
					</svg> Scan Distance</li>
				<li><svg viewBox="0 0 24 24" width="16" height="16" stroke="firebrick" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1">
						<circle cx="12" cy="12" r="10"></circle>
						<circle cx="12" cy="12" r="6"></circle>
						<circle cx="12" cy="12" r="2"></circle>
					</svg> Bullet Power</li>
			</ul>
			You can give any number of points (up to 5) to each power, but you only have 5 points to give.
			<br />
			<small>**note**: If you go over 5 total points...
				<br />
				(ex. {armor: 3, scanDistance:2, bulletPower:2})
				<br />
				the fighting arena will set all 3 of your bot's powers to <i>0</i>.</small>
			<br />
			<br />
			<hr />
			<h3>Location</h3>
			In addition to having powers to balance, your bot will have access to the 2 following bot functions:
			<ul>
				<li>
					<b>this.scan()</b>
					<br />
					<small>will either return:
						<ol>
							<li>an object with the enemy's x and y (if an enemy is within the scanRadius)</li>
							<li>an empty object (if no enemy within the radius)</li>
						</ol>
					</small>
				</li>
				<li>
					<b>this.whereAmI()</b>
					<br />
					<small>will return your x and y in an object (ex: {x: 25, y: 61})</small>
				</li>
			</ul>
			<br />
			<hr />
			<h3>Logic</h3>
			The logic for your brain will occur within your <b>decide()</b> function.
			<br />
			<small>This is the function that is triggered every 100ms by the bot</small>
			<br />
			Inside of <i>decide()</i> is where you change the values of:
			<ul>
				<li><b>this.decision</b> <small>("shoot", "up", "down", "left", "right")</small></li>
				<li><b>this.shotAngle</b> <small>(between 0 and 360. Default is 0)</small></li>
			</ul>
			<br />
			<hr />
			<h3>Example Brain</h3>
			<pre class="language-javascript">class Brain {
	constructor() {
		this.name = "Nameless Bot";
		this.color = "purple";
		this.armor = 0;
		this.scanDistance = 0;
		this.bulletPower = 0;

		// decide() should change these 2
		this.decision = "shoot";
		this.shotAngle = 0;

		/* 
		you can add additional 
		variables here if you want
		*/
	}

	/* 
	these calls are available any time 
	and do NOT take the place of a decision
	
	this.scan();
	will either return:
	1. an object with the enemy's x and y 
	(if an enemy is within the scan radius)
	2. an empty object 
	(if no enemy within the radius)
	
	this.whereAmI();
	will return your x and y in an object 
	(ex: {x: 25, y: 61})
	*/

	// the decide call gets made 
	// every time the game updates 
	// (e.g. every 100ms)
	decide() {
		// decision-making logic here 
		// (e.g., move left, shoot, etc.)
		return this.decision;
	}
}</pre>
			<br />
		</div>
	</dialog>
	<br />
	<div class="btnContainer">
		<button id="import">Import Player</button>
		<dialog>
			<h3 class="header">Import Player</h3>
			<button type="button" class="closeDialog" title="Close">x</button>
			<div class="gameInfo">
				<input type="text" id="urlInput" placeholder="Enter URL of JS file">
				<button class="import">Add Player</button>
				<br />
				<h6>Example import:</h6>
				<span class="playerLink"><small>https://repo.bfw.wiki/bfwrepo/js/game1/Bojangles.js</small></span>
				<br />
				<br />
				<hr />
				Your imported player should be the only thing that exists in the .js file you will import and should look like the following:
				<br />
				<pre class="language-javascript">export default class MyBrain {
	constructor() {
		this.name = "Big Brain Bot";
		this.color = "purple";
		this.armor = 0;
		this.scanDistance = 0;
		this.bulletPower = 0;

		// decide() should change these 2
		this.decision = "shoot";
		this.shotAngle = 0;

		/* 
		you can add additional 
		variables here if you want
		*/
	}

	/* 
	these calls are available any time 
	and do NOT take the place of a decision
	
	this.scan();
	will either return:
	1. an object with the enemy's x and y 
	(if an enemy is within the scan radius)
	2. an empty object 
	(if no enemy within the radius)
	
	this.whereAmI();
	will return your x and y in an object 
	(ex: {x: 25, y: 61})
	*/

	// the decide call gets made 
	// every time the game updates 
	// (e.g. every 100ms)
	decide() {
		// decision-making logic here 
		// (e.g., move left, shoot, etc.)
		return this.decision;
	}
}</pre>
				
				<br />
				<br />
				<div class="importedPlayers">
					<h5>Imported Players</h5>
					<hr />
				</div>
			</div>
		</dialog>
		<br />
		<br />
		Game Speed (ms):
		<div class="gameSpeed">
			<input type="range" min="10" max="300" value="100" step="10" class="slider" id="speedRange">
			<br />
			<small><span class="gameSpeedMs">100</span> ms</small>
		</div>
		<div>
			<hr />
			<br />
			Bullet Color: <input type="color" id="bulletCol" value="#FF69B4">
		</div>
		<br />
		<hr />
		<br />
		<div>
			Shield Color: <input type="color" id="shieldCol" value="#00FFFF">
		</div>
		<br />
		<hr />
		<br />
		Log:
		<br />
		<div id="log"><span>Log messages populate here</span></div>
		<br />
	</div>

</div>

<div class="container">

	<svg id="arena" viewBox="0 0 100 100">
		<radialGradient id="grad">
			<stop offset="0%" stop-color="#000" />
			<stop offset="100%" stop-color="#ff8c00" />
		</radialGradient>
		<pattern id="mech" width="310" height="180" patternUnits="userSpaceOnUse" patternTransform="scale(.05 .05) skewY(10) skewX(-10)">
			<rect height="180" width="310" opacity="1" fill="url(#grad)" />
			<g id="t">
				<g id="tl">
					<path class="r" fill="rgb(40,40,40)" d="M 155 90 L 105 0 L 155 -90" />
					<path fill="rgb(30,30,30)" d="M 155 90 L 205 0 L 155 -90" />
					<path stroke="rgb(20,20,20)" d="M 155 180 V -90" />
				</g>
				<use href="#tl" transform="translate(0 180)" />
			</g>

			<use href="#tl" transform="translate(155 90)" />
			<use href="#tl" transform="translate(-155 90)" />
			<use href="#t" transform="rotate(-60 155 90)" filter="brightness(90%)" />
			<use href="#t" transform="rotate(-120 155 90)" filter="brightness(90%)" />
		</pattern>
		<rect width="100%" height="100%" fill="#222" opacity="0.2" />
		<rect width="100%" height="100%" fill="url(#mech)" />
		<text font-family="'Roboto', sans-serif" x="50" y="50" text-anchor="middle" dominant-baseline="middle" font-size="15" font-weight="900" fill="goldenrod" opacity="0.9">Robo Wars
			<animate attributeName="x" values="57%; 43%; 57%" dur="5s" begin="0s" repeatCount="indefinite" />
			<animate attributeName="y" values="93%; 7%; 93%" dur="15s" begin="-6s" repeatCount="indefinite" />
		</text>
		<!-- Bot and projectile elements will be dynamically created here -->
	</svg>

	<div class="matchupContainer">
		<select name="bots1" class="selectedFighter s1" id="bot-select-1" >
			<!--Bots populate here-->
		</select>
		<span class="versus">vs.</span>
		<select name="bots2" class="selectedFighter s2" id="bot-select-2">
			<!--Bots also populate here-->
		</select>
	</div>
	<br />

	<br />
	<button class="endBtn" title="End Game">END</button>
	<button class="startBtn" title="Start Game">START</button>
</div>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/prism.js"></script>
      <script type="module">
const arenaSVG = document.getElementById("arena");

let frameInterval = 100; // Update frame every 100ms
let bulletColor = "#FF69B4";
let shieldColor = "#0FF";
const gameBG = `<filter id="blur">
		<feGaussianBlur stdDeviation="1" />
	</filter>
	<pattern id="rubber" width="20" height="15" patternUnits="userSpaceOnUse" patternTransform="scale(.1 .1)">
		<g filter="url(#blur)" fill="#000">
			<circle cx="9.8" cy="7.3" r="4" />
			<circle cx="19.8" cy="14.8" r="4" />
			<circle cx="-0.2" cy="14.8" r="4" />
			<circle cx="-0.2" cy="-0.2" r="4" />
			<circle cx="19.8" cy="-0.2" r="4" />
		</g>
		<g filter="url(#blur)" fill="#666">
			<circle cx="10.2" cy="7.7" r="4" />
			<circle cx="20.2" cy="15.2" r="4" />
			<circle cx="0.2" cy="15.2" r="4" />
			<circle cx="0.2" cy=".2" r="4" />
			<circle cx="20.2" cy=".2" r="4" />
		</g>
		<g fill="#111">
			<circle cx="20" cy="15" r="4" />
			<circle cx="0" cy="15" r="4" />
			<circle cx="0" cy="0" r="4" />
			<circle cx="10" cy="7.5" r="4" />
			<circle cx="20" cy="0" r="4" />
		</g>
	</pattern>
	<rect width="100%" height="100%" fill="#222" />
	<rect width="100%" height="100%" fill="url(#rubber)" />`;

let isGameRunning = false;

function changeFrameInterval(val) {
  frameInterval = val;
  document.querySelector(".gameSpeedMs").innerText = val;
}

class Arena {
  constructor(width, height) {
    this.width = width;
    this.height = height;
    this.bots = [];
    this.projectiles = [];
  }

  // Method to get the bots in the arena
  getBots() {
    return this.bots;
  }}


// Initialize the game
let arena = new Arena(100, 100);

// Projectile linked list implementation
class ProjectileNode {
  constructor(projectile) {
    this.projectile = projectile;
    this.next = null;
  }}


class ProjectileLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Add a new projectile to the end of the linked list
  add(projectile) {
    const newNode = new ProjectileNode(projectile);

    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      this.tail.next = newNode;
      this.tail = newNode;
    }

    this.length++;
  }

  // Remove a projectile from the linked list
  remove(projectile) {
    let currentNode = this.head;
    let prevNode = null;

    while (currentNode) {
      if (currentNode.projectile === projectile) {
        if (currentNode === this.head) {
          this.head = currentNode.next;
        } else if (currentNode === this.tail) {
          this.tail = prevNode;
          prevNode.next = null;
        } else {
          prevNode.next = currentNode.next;
        }

        this.length--;
        break;
      }

      prevNode = currentNode;
      currentNode = currentNode.next;
    }
  }

  // Method to remove all projectiles from the linked list
  removeAll() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Get the size of the linked list
  size() {
    return this.length;
  }

  // Iterate through the linked list and call a callback function on each node
  forEach(callback) {
    let currentNode = this.head;

    while (currentNode) {
      callback(currentNode.projectile);
      currentNode = currentNode.next;
    }
  }}


// Bot class not editable by the Brain Makers
class Bot {
  constructor(x, y, brain) {
    // eachg bot needs a unique ID
    const getUUID = () => {
      let d = new Date().getTime();
      let uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
      /[xy]/g,
      function (c) {
        let r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c == "x" ? r : r & 0x3 | 0x8).toString(16);
      });

      return uuid;
    };
    this.id = getUUID();
    this.x = x;
    this.y = y;
    this.r = 2;
    this.brain = brain;

    // Add a property to track the decision counter
    this.decisionCounter = 0;

    // Initial bullet count
    this.bulletCount = 10;

    // check em before we assign
    this.checkPowerValues();

    // Initial armor value (if 0, armor = 100)
    this.armor = (this.brain.armor || 0) * 25 + 100;
    // Initial bullet power
    this.bulletPower = this.brain.bulletPower || 0;
    // Initial scan radius
    this.scanRadius = (this.brain.scanDistance || 0) * 20;

    // create svg elements
    this.botElement = this.createBotElement();
    this.shieldElement = this.createShieldElement();
    this.scanElement = this.createScanElement();
    this.bandolierElement = this.createBandolierElement();
    this.projectilesList = new ProjectileLinkedList();

    // Create a closure to encapsulate the scan function
    const scan = () => {
      const enemyPositions = arena.bots.
      filter(bot => bot !== this && this.isInScanRange(bot.x, bot.y)).
      map(bot => ({ x: bot.x, y: bot.y }));
      if (enemyPositions.length > 0) {
        logMe(
        `${this.brain.name} scanned and found ${JSON.stringify(enemyPositions[0])}`);

      }

      return enemyPositions;
    };

    // Expose only the scan function to the Brain
    this.brain.scan = scan;

    // Create a getter function for whereAmI
    const whereAmI = () => {
      return { x: this.x, y: this.y };
    };

    // Add the whereAmI function to the existing brain object
    this.brain.whereAmI = whereAmI;
  }

  // this will punish you if you tried to give your bot too many points
  checkPowerValues() {
    function hasPower(power) {
      if (!power) {
        return 0;
      }
      return power;
    }
    function inBounds(power) {
      if (power >= 6 || power <= -1) {
        return false;
      }
      return true;
    }
    if (
    hasPower(this.brain.armor) +
    hasPower(this.brain.bulletPower) +
    hasPower(this.brain.scanDistance) >=
    6 ||
    !inBounds())
    {
      this.brain.armor = 0;
      this.brain.bulletPower = 0;
      this.brain.scanDistance = 0;
    }
  }

  // create the circle that represents the bot
  createBotElement() {
    const botElement = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "circle");

    botElement.classList.add("bot");
    botElement.setAttribute("r", this.r);
    botElement.setAttribute("cx", this.x);
    botElement.setAttribute("cy", this.y);
    botElement.setAttribute("fill", this.brain.color); // Set the color of the bot circle
    botElement.setAttribute("data-name", this.brain.name);
    arenaSVG.appendChild(botElement);
    return botElement;
  }

  // create the other circle that represents the shield element
  createShieldElement() {
    const shieldElement = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "circle");

    let circumference = 2 * Math.PI * (this.r + 1.7);
    shieldElement.classList.add("shield");
    shieldElement.setAttribute("r", this.r + 1.7);
    shieldElement.setAttribute("cx", this.x);
    shieldElement.setAttribute("cy", this.y);
    shieldElement.setAttribute("fill", "none");
    shieldElement.setAttribute("stroke", shieldColor);
    shieldElement.setAttribute("opacity", "0.7");
    shieldElement.setAttribute("stroke-width", `1.5`);
    shieldElement.setAttribute("stroke-dashoffset", -circumference / 4);
    shieldElement.setAttribute("stroke-linecap", `round`);
    shieldElement.setAttribute(
    "stroke-dasharray",
    `0 ${circumference / (this.armor / 25)}`);

    // botElement.setAttribute("data-name", this.brain.name);
    arenaSVG.appendChild(shieldElement);
    return shieldElement;
  }
  // create the other circle that represents the bandolier element
  createBandolierElement() {
    const bandElement = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "circle");

    let circumference = 2 * Math.PI * (this.r + 0.5);
    bandElement.classList.add("bandolier");
    bandElement.setAttribute("r", this.r + 0.5);
    bandElement.setAttribute("cx", this.x);
    bandElement.setAttribute("cy", this.y);
    bandElement.setAttribute("fill", "none");
    bandElement.setAttribute("stroke", bulletColor);
    bandElement.setAttribute("opacity", "0.8");
    bandElement.setAttribute("stroke-width", `1.1`);
    bandElement.setAttribute("stroke-dashoffset", circumference / 4);
    bandElement.setAttribute("stroke-linecap", `round`);
    bandElement.setAttribute(
    "stroke-dasharray",
    `0 ${circumference / this.bulletCount}`);

    // botElement.setAttribute("data-name", this.brain.name);
    arenaSVG.appendChild(bandElement);
    return bandElement;
  }

  // create the other circle that represents the shield element
  createScanElement() {
    const scanElement = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "circle");

    let circumference = 2 * Math.PI * (this.brain.scanDistance * 20);
    scanElement.classList.add("scan");
    scanElement.setAttribute("r", this.brain.scanDistance * 20);
    scanElement.setAttribute("cx", this.x);
    scanElement.setAttribute("cy", this.y);
    scanElement.setAttribute("fill", "none"); // Set the color of the bot circle
    scanElement.setAttribute("stroke", "rgba(0,255,255,0.3)"); // Set the color of the bot circle
    scanElement.setAttribute("stroke-width", `0.1`);
    // botElement.setAttribute("data-name", this.brain.name);
    arenaSVG.appendChild(scanElement);
    return scanElement;
  }

  // Function to check if a given point (x, y) falls within the scan radius of the bot
  isInScanRange(x, y) {
    const distance = Math.sqrt((x - this.x) ** 2 + (y - this.y) ** 2);
    return distance <= this.scanRadius;
  }

  move() {
    // Increment the decision counter
    this.decisionCounter++;

    // Implement decide() method in the Brain class
    const action = this.brain.decide();

    // Check if the bot can fire based on the decision counter and remaining bullets
    if (this.decisionCounter % 5 == 0 && this.bulletCount <= 10) {
      this.bulletCount++;
    }

    if (action == "shoot" && this.bulletCount <= 0) {
      return;
    }

    // Update the bot's position based on the action (e.g., action = 'left', move left)
    switch (action) {
      case "left":
        this.x -= 1;
        break;
      case "right":
        this.x += 1;
        break;
      case "up":
        this.y -= 1;
        break;
      case "down":
        this.y += 1;
        break;
      case "shoot":
        this.shoot(this.brain.shotAngle);
        this.bulletCount--;
        break;
      // Add more cases for other actions as needed
    }

    // Ensure the bot stays within the viewBox boundaries
    this.x = Math.max(0, Math.min(this.x, 100 - this.r));
    this.y = Math.max(0, Math.min(this.y, 100 - this.r));

    // Update the position of the bot circle in the SVG
    this.botElement.setAttribute("cx", this.x);
    this.botElement.setAttribute("cy", this.y);
    this.shieldElement.setAttribute("cx", this.x);
    this.shieldElement.setAttribute("cy", this.y);
    this.bandolierElement.setAttribute("cx", this.x);
    this.bandolierElement.setAttribute("cy", this.y);
    this.scanElement.setAttribute("cx", this.x);
    this.scanElement.setAttribute("cy", this.y);
  }

  // Function to handle taking damage
  gotHit(damage) {
    this.armor -= damage;
    logMe(`${this.brain.name} is at ${this.armor} health!`);
    if (this.armor <= 0) {
      // Bot destroyed, handle accordingly (e.g., remove from SVG)
      this.destroy();
    } else {
      // Change the bot's color when it gets hit
      this.botElement.setAttribute("fill", "yellow");
      this.botElement.setAttribute("stroke", "rgba(255,0,0,0.8)");
      this.botElement.setAttribute("stroke-width", 0.5);
      this.shieldElement.setAttribute(
      "stroke-dasharray",
      `0 ${2 * Math.PI * (this.r + 1.7) / (this.armor / 25)}`);

      // Reset the bot's color after a delay
      setTimeout(() => {
        this.botElement.setAttribute(&qu.........完整代码请登录后点击上方下载按钮下载查看

网友评论0