基本上,我正在尝试不使用任何库来制作一个自玩的JavaScript棒球游戏。
我能做些什么吗?在代码中进行改进吗?
var writeDown = {
delay: 110,
add: null,
div: document.getElementById('playArea'),
log: function() {
var args = arguments;
if (args.length == 0) {
args = [''];
}
var div = this.div;
setTimeout(function() {
//console.log(args[0]);
div.innerHTML = args[0] + "<br/>" + div.innerHTML;
}, this.delay);
if (this.add == null) {
this.add = this.delay;
}
this.delay += this.add;
},
updateDiv: function(msg, div) {
setTimeout(function() {
//console.log(msg, div);
div.innerHTML = msg;
}, this.delay);
},
updateDiv_delay: function(msg, div) {
setTimeout(function() {
document.getElementById(div).innerHTML = msg;
}, this.delay);
},
updateDiv_color: function(color, div) {
setTimeout(function() {
//console.log(color, div);
document.getElementById(div).style.background = color;
}, this.delay);
}
}
var Diamond = function(d_name, d_id) {
var name = d_name;
var diamond = document.getElementById(d_id);
var bases = {
first: false,
second: false,
third: false,
home: false
};
var players = {
first: false,
second: false,
third: false,
home: false
};
this.clear = function() {
bases = {
first: false,
second: false,
third: false,
home: false
};
players = {
first: false,
second: false,
third: false,
home: false
};
this.updateBases();
writeDown.updateDiv('', diamond);
}
this.onBase = function(base_amt, PlayerName) {
var return_runs = 0;
switch (base_amt) {
case 0:
return 0;
break;
case 1:
if (bases.first) {
if (bases.second) {
if (bases.third) {
writeDown.log(name + ": BASES LOADED, " + players.third + " scored");
return_runs += 1;
}
else {
writeDown.log(name + ": BASES LOADED");
bases.third = true;
}
players.third = players.second;
players.second = players.first;
players.first = PlayerName;
}
else {
writeDown.log(name + ": Man on 1st and 2nd");
players.second = players.first;
players.first = PlayerName;
bases.second = true;
}
}
else {
writeDown.log(name + ": Man on 1st");
players.first = PlayerName;
bases.first = true;
}
break;
case 2:
if (bases.first) {
writeDown.log(name + ": Man on 2nd, and Third");
bases.first = false;
if (bases.third) return_runs += 1;
bases.third = true;
}
if (bases.second) {
return_runs += 1;
if (bases.third) {
writeDown.log(name + ": Man on 2nd, 2 runs scored (" + players.second + ", " + players.third + ")");
bases.third = false;
return_runs += 1;
}
else {
writeDown.log(name + ": Man on 2nd, run scored (" + players.second + ")");
}
}
if (bases.third) {
return_runs += 1;
bases.third = false;
writeDown.log(name + ": Man on 2nd, run scored (" + players.third + ")");
}
else {
writeDown.log(name + ": Man on 2nd");
bases.second = true;
}
players.third = players.first;
players.second = PlayerName;
players.first = null;
break;
case 3:
if (bases.first) {
writeDown.log(name + ": " + players.first + " Scored from 1st");
bases.first = false;
return_runs += 1;
}
if (bases.second) {
writeDown.log(name + ": " + players.second + " Scored from 2nd");
bases.second = false;
return_runs += 1;
}
if (bases.third) {
writeDown.log(name + ": " + players.third + " Scored from 3rd");
return_runs += 1;
}
else {
writeDown.log(name + ": Man on 3rd");
bases.third = true;
}
players.third = PlayerName;
players.second = null;
players.first = null;
break;
case 4:
if (bases.first) {
writeDown.log(name + ": " + players.first + " Scored from 1st");
bases.first = false;
return_runs += 1;
}
if (bases.second) {
writeDown.log(name + ": " + players.second + " Scored from 2nd");
bases.second = false;
return_runs += 1;
}
if (bases.third) {
writeDown.log(name + ": " + players.third + " Scored from 3rd");
bases.third = false;
return_runs += 1;
}
players.third = null;
players.second = null;
players.first = null;
writeDown.log(name + ": " + PlayerName + " Scored from home");
return_runs += 1;
break;
}
var man_on = "",
base_names = ['first', 'second', 'third'];
for (var i = 0; i < 4; i++) {
if (players[base_names[i]] != null && players[base_names[i]]) {
man_on += players[base_names[i]] + " is on " + base_names[i] + " base <br/>";
}
}
writeDown.updateDiv(man_on, diamond);
this.updateBases();
return return_runs;
}
this.updateBases = function() {
for (base in bases) {
if (bases[base] == true) {
writeDown.updateDiv_color('#F00', base);
}
else {
writeDown.updateDiv_color('#AAA', base);
}
}
}
this.playGame = function(TeamA, TeamB, innings) {
var score_div = document.getElementById('score');
writeDown.updateDiv(TeamA.name + ": <span id='" + TeamA.name + "'>" + TeamA.getScore() + "</span><br/>" + TeamB.name + ": <span id='" + TeamB.name + "'>" + TeamB.getScore() + "</span><hr>" + "Outs: <span id='outs'>0</span><br/>" + "Inning: <span id='inning'>1</span>", score_div);
for (var i = 0; i < innings; i++) {
writeDown.log("<br/><b>INNING " + (i + 1) + "</b><br/>");
writeDown.updateDiv_delay("Top of " + (i + 1), 'inning');
if (TeamA.teamUp()) {
writeDown.updateDiv_delay("Bottom of " + (i + 1), 'inning');
writeDown.log(TeamA.name + " are out <br/>");
this.clear();
TeamA.resetOuts();
writeDown.log("");
if (TeamB.teamUp()) {
writeDown.log(TeamB.name + " are out<br/><br/>");
this.clear();
TeamB.resetOuts();
writeDown.log("");
}
}
}
}
}
var Player = function(pitcher, name) {
var name = (name == undefined) ? "Nothing" : name;
var balls = 0;
var strikes = 0;
this.getName = function() {
return name;
}
this.atBat = function() {
var pitch = pitcher.show_pitch();
var random = Math.floor(Math.random() * 1000);
var swing_rate = 500 - (75 * strikes);
if (random < swing_rate) { //swing
strikes += 1;
writeDown.log(name + " swung and missed.");
writeDown.log(name + " has " + strikes + " strikes.");
if (strikes >= 3) {
strikes = 0;
balls = 0;
writeDown.log(name + " struck out");
return {
out: 1,
type: 0
};
}
}
else if (random < 880) { //wait for pitch
writeDown.log(name + " watches the pitch.");
if (pitch == "Strike") {
strikes += 1;
writeDown.log(name + " has " + strikes + " strikes.");
if (strikes >= 3) {
strikes = 0;
balls = 0;
writeDown.log(name + " struck out");
return {
out: 1,
type: 0
};
}
}
if (pitch == "Ball") {
balls += 1;
writeDown.log(name + " has " + balls + " balls.");
if (balls >= 4) {
balls = 0;
strikes = 0;
writeDown.log(name + " has been walked");
return {
out: 0,
type: 1
};
}
}
}
else if (random <= 1000) { //hit ball
balls = 0;
strikes = 0;
var hit = "Single";
if (random > 940 && random < 970) {
hit = "Double";
}
else if (random >= 970 && random < 995) {
hit = "Triple";
}
else if(random >= 995){
hit = "Homerun";
}
writeDown.log(name + " hit a " + hit);
var hit_type = 1;
if (hit == "Double") hit_type = 2;
if (hit == "Triple") hit_type = 3;
if (hit == "Homerun") hit_type = 4;
writeDown.log(name + " going to base");
return {
out: 0,
type: hit_type
};
}
//writeDown.log(name + " waiting for next pitch");
return this.atBat();
}
}
var Pitcher = function(team) {
var types = ["Ball", "Strike"];
var Team = team;
this.show_pitch = function() {
var random = Math.floor(Math.random() * (types).length);
writeDown.log();
writeDown.log(Team.name + " pitcher threw the ball.");
return types[random];
}
}
var Team = function() {
var amt_of_players = 9;
var players = [];
var pitcher = new Pitcher(this);
var otherPitcher = null;
var outs = 0;
var score = 0;
var stadium = null;
var player_up_to = 0;
this.name = "Nobody's";
this.createTeam = function(TeamName, Opponent, Diamond) {
stadium = Diamond;
otherPitcher = Opponent.getPitcher();
this.name = (TeamName == undefined) ? "Nothing" : TeamName;
for (var i = 0; i < amt_of_players; i++) {
players[i] = new Player(otherPitcher, "Player " + (i + 1) + " on " + this.name);
}
return this;
}
this.teamUp = function() {
for (var i = player_up_to; i < players.length; i++) {
var atBat = players[i].atBat();
outs += atBat.out;
score += stadium.onBase(atBat.type, players[i].getName());
writeDown.updateDiv_delay(score, this.name);
writeDown.updateDiv_delay(outs, 'outs');
if (outs >= 3) {
player_up_to = (i + 1) % players.length; //start with next player;
return true;
}
}
if (outs >= 3) {
player_up_to = 0;
return true;
}
else {
player_up_to = 0;
return this.teamUp();
}
}
this.getScore = function() {
return score;
}
this.resetOuts = function() {
outs = 0;
writeDown.updateDiv_delay(outs, 'outs');
}
this.getPitcher = function() {
return pitcher;
}
}
var TeamA = new Team();
var TeamB = new Team();
var Stadium = new Diamond("Citi Field", 'move');
TeamA.createTeam("Yankees", TeamB, Stadium);
TeamB.createTeam("Mets", TeamA, Stadium);
Stadium.playGame(TeamA, TeamB, 9);
writeDown.log("GAME OVER!");
#1 楼
var writeDown = {
delay: 110,
add: null,
div: document.getElementById('playArea'),
log: function() {
var args = arguments;
if (args.length == 0) {
args = [''];
}
var div = this.div;
setTimeout(function() {
//console.log(args[0]);
div.innerHTML = args[0] + "<br/>" + div.innerHTML;
}, this.delay);
你在用参数做什么?您似乎正在采用一种about回方法,使log可以选择采用单个参数。但是尚不清楚为什么不带任何参数的日志记录会有意义。
if (this.add == null) {
this.add = this.delay;
}
this.delay += this.add;
您已通过安排一系列事件来进行此操作,这些事件将在以后进行。这有点聪明,但不是您应该在JavaScript上下文中执行的操作。更好的方法是对代码进行结构化,以便重复调用一个函数来执行逻辑,从而使处理正在进行而不是预先完成。
},
updateDiv: function(msg, div) {
setTimeout(function() {
//console.log(msg, div);
div.innerHTML = msg;
}, this.delay);
},
updateDiv_delay: function(msg, div) {
setTimeout(function() {
document.getElementById(div).innerHTML = msg;
}, this.delay);
},
这些希望将非常相似的功能以及上面的相似代码进行组合。
updateDiv_color: function(color, div) {
setTimeout(function() {
//console.log(color, div);
document.getElementById(div).style.background = color;
}, this.delay);
}
}
var Diamond = function(d_name, d_id) {
var name = d_name;
var diamond = document.getElementById(d_id);
var bases = {
first: false,
second: false,
third: false,
home: false
};
当元素具有顺序解释时,通常最好使用数字而不是名称。基应该是数组而不是对象。
var players = {
first: false,
second: false,
third: false,
home: false
};
而不是像这样存储玩家,而是将所有内容都放入bases数组中。对于没有玩家,请使用null或false,否则,请使用玩家的名称。
this.clear = function() {
bases = {
first: false,
second: false,
third: false,
home: false
};
players = {
first: false,
second: false,
third: false,
home: false
};
this.updateBases();
writeDown.updateDiv('', diamond);
}
您应该在此构造函数内使用clear函数,以便不必重复其构造函数内容。
this.onBase = function(base_amt, PlayerName) {
使用variable_like_this或VariablesLikeThis或variableLikeThis。避免混合样式。同样,base_amt并不清楚它的含义。
var return_runs = 0;
switch (base_amt) {
...
您用来确定新基准和运行的代码太复杂了。您的代码应类似于:
for each base
if man on base, move forward base_amt places
if move is past home, score a run
otherwise set new position
您永远不需要编写上面编写的重复代码。
var man_on = "",
base_names = ['first', 'second', 'third'];
for (var i = 0; i < 4; i++) {
if (players[base_names[i]] != null && players[base_names[i]]) {
man_on += players[base_names[i]] + " is on " + base_names[i] + " base <br/>";
}
}
如果您就使用数组而不是对象来跟踪基数提出建议,那么此代码将更加简单。
writeDown.updateDiv(man_on, diamond);
this.updateBases();
return return_runs;
}
this.updateBases = function() {
for (base in bases) {
if (bases[base] == true) {
==正确,只需使用if(bases [base]
writeDown.updateDiv_color('#F00', base);
}
else {
writeDown.updateDiv_color('#AAA', base);
}
}
}
this.playGame = function(TeamA, TeamB, innings) {
var score_div = document.getElementById('score');
writeDown.updateDiv(TeamA.name + ": <span id='" + TeamA.name + "'>" + TeamA.getScore() + "</span><br/>" + TeamB.name + ": <span id='" + TeamB.name + "'>" + TeamB.getScore() + "</span><hr>" + "Outs: <span id='outs'>0</span><br/>" + "Inning: <span id='inning'>1</span>", score_div);
for (var i = 0; i < innings; i++) {
writeDown.log("<br/><b>INNING " + (i + 1) + "</b><br/>");
writeDown.updateDiv_delay("Top of " + (i + 1), 'inning');
if (TeamA.teamUp()) {
据我所知,teamUp永远不会返回false。它也是骗人的,因为我希望这样的事情能够回答一个没有完整的半局的问题。名称或使用方式均不暗示该功能可以做到这一点。
writeDown.updateDiv_delay("Bottom of " + (i + 1), 'inning');
writeDown.log(TeamA.name + " are out <br/>");
this.clear();
TeamA.resetOuts();
writeDown.log("");
if (TeamB.teamUp()) {
writeDown.log(TeamB.name + " are out<br/><br/>");
this.clear();
TeamB.resetOuts();
writeDown.log("");
}
}
}
}
}
var Player = function(pitcher, name) {
var name = (name == undefined) ? "Nothing" : name;
var balls = 0;
var strikes = 0;
this.getName = function() {
return name;
}
this.atBat = function() {
var pitch = pitcher.show_pitch();
var random = Math.floor(Math.random() * 1000);
var swing_rate = 500 - (75 * strikes);
if (random < swing_rate) { //swing
strikes += 1;
writeDown.log(name + " swung and missed.");
writeDown.log(name + " has " + strikes + " strikes.");
if (strikes >= 3) {
strikes = 0;
balls = 0;
writeDown.log(name + " struck out");
return {
out: 1,
type: 0
};
}
}
else if (random < 880) { //wait for pitch
writeDown.log(name + " watches the pitch.");
if (pitch == "Strike") {
strikes += 1;
writeDown.log(name + " has " + strikes + " strikes.");
if (strikes >= 3) {
strikes = 0;
balls = 0;
writeDown.log(name + " struck out");
return {
out: 1,
type: 0
};
}
}
if (pitch == "Ball") {
balls += 1;
writeDown.log(name + " has " + balls + " balls.");
if (balls >= 4) {
balls = 0;
strikes = 0;
writeDown.log(name + " has been walked");
return {
out: 0,
type: 1
};
}
}
您可以在两个地方检查删除线的代码。您应该将该代码移出if(random)块,以便可以共享。
}
else if (random <= 1000) { //hit ball
balls = 0;
strikes = 0;
var hit = "Single";
if (random > 940 && random < 970) {
hit = "Double";
}
else if (random >= 970 && random < 995) {
hit = "Triple";
}
else if(random >= 995){
hit = "Homerun";
}
writeDown.log(name + " hit a " + hit);
var hit_type = 1;
if (hit == "Double") hit_type = 2;
if (hit == "Triple") hit_type = 3;
if (hit == "Homerun") hit_type = 4;
writeDown.log(name + " going to base");
return {
out: 0,
type: hit_type
};
}
//writeDown.log(name + " waiting for next pitch");
return this.atBat();
}
}
如果您分离出逻辑判断是否要执行以下操作,则此函数会更简单这是一次罢工/球/单/双/三联/本垒打,并决定了后果是什么。
var Pitcher = function(team) {
var types = ["Ball", "Strike"];
var Team = team;
this.show_pitch = function() {
var random = Math.floor(Math.random() * (types).length);
writeDown.log();
writeDown.log(Team.name + " pitcher threw the ball.");
return types[random];
}
}
var Team = function() {
var amt_of_players = 9;
var players = [];
var pitcher = new Pitcher(this);
var otherPitcher = null;
var outs = 0;
var score = 0;
var stadium = null;
var player_up_to = 0;
this.name = "Nobody's";
this.createTeam = function(TeamName, Opponent, Diamond) {
stadium = Diamond;
otherPitcher = Opponent.getPitcher();
this.name = (TeamName == undefined) ? "Nothing" : TeamName;
for (var i = 0; i < amt_of_players; i++) {
players[i] = new Player(otherPitcher, "Player " + (i + 1) + " on " + this.name);
}
return this;
}
传球的对手有点奇怪这里。这意味着一个团队只能有一个对手,这并不代表团队的运作方式。
this.teamUp = function() {
for (var i = player_up_to; i < players.length; i++) {
var atBat = players[i].atBat();
outs += atBat.out;
score += stadium.onBase(atBat.type, players[i].getName());
writeDown.updateDiv_delay(score, this.name);
writeDown.updateDiv_delay(outs, 'outs');
if (outs >= 3) {
player_up_to = (i + 1) % players.length; //start with next player;
return true;
}
}
if (outs >= 3) {
player_up_to = 0;
return true;
}
else {
player_up_to = 0;
return this.teamUp();
}
}
this.getScore = function() {
return score;
}
this.resetOuts = function() {
outs = 0;
writeDown.updateDiv_delay(outs, 'outs');
}
this.getPitcher = function() {
return pitcher;
}
}
var TeamA = new Team();
var TeamB = new Team();
var Stadium = new Diamond("Citi Field", 'move');
TeamA.createTeam("Yankees", TeamB, Stadium);
TeamB.createTeam("Mets", TeamA, Stadium);
Stadium.playGame(TeamA, TeamB, 9);
writeDown.log("GAME OVER!");
评论
\ $ \ begingroup \ $
@Winston,您将如何处理日志?
\ $ \ endgroup \ $
– Naftali又名Neal
2011年5月27日下午5:44
\ $ \ begingroup \ $
@Winston,我需要通过对手的唯一原因是因为我的Player对象需要知道谁是对方投手,我还能怎么做?
\ $ \ endgroup \ $
– Naftali又名Neal
2011年5月27日下午5:48
\ $ \ begingroup \ $
@Neal,回复:是在处理参数时还是在您不断构建的数组方面进行登录?
\ $ \ endgroup \ $
–温斯顿·埃韦特(Winston Ewert)
2011年5月27日13:36
\ $ \ begingroup \ $
@Neal,我会将投手作为参数传递给您的teamUp方法。
\ $ \ endgroup \ $
–温斯顿·埃韦特(Winston Ewert)
2011年5月27日13:37
\ $ \ begingroup \ $
@温斯顿,我的意图是让每支球队都有自己的投手,所以对方的球队必须知道对方的投手
\ $ \ endgroup \ $
– Naftali又名Neal
2011年5月27日下午14:37
#2 楼
Javascript不是我的主要语言,我也不是程序员。话虽如此,这主要是关于伪造我如何构造游戏循环的伪方法。var game = Coaches.StartGame()
while(game.IsPlaying)
{
var pitcher = game.CurrentPitcher;
var batter = game.CurrentBatter;
var ball = pitcher.Pitch();
var swing = batter.HandlePitch(ball);
var decision = Umpire.HandleSwing(swing);
var isInningOver = Umpire.CallInning(decision, game);
if(isInningOver)
{
var isGameOver = Umpire.CallGame(decision, game);
if(isGameOver)
{
game = Umpire.EndGame(game);
}
else
{
game = Coaches.AdvaceInning(game);
}
}
else
{
var field = Coaches.SendBaseRunners(decision, game)
game = Umpire.Advance(field);
//theoretically at this point you could need to check whether game/inning ended
//from base runners getting thrown out if you want to support that
game = Coaches.NextBatter(game);
}
}
此后,您将有教练开始比赛并派遣球员,裁判所属的游戏。
此允许您在没有任何单一责任原则冲突的情况下拥有游戏逻辑,而我上面显示的任何方法都无需出于任何原因而进行更改除了其特定的逻辑。
这与您当前的设置有所不同,当前的设置具有共同的责任,例如atBat方法既可以为击球得分打分,又可以为游戏打分。
评论
\ $ \ begingroup \ $
除了将您的while循环替换为window.setTimeout()循环(不会导致“此脚本花费的时间太长”弹出窗口)之外,这正是我的方法。 +1
\ $ \ endgroup \ $
–比尔·巴里
2012年4月30日在22:17
\ $ \ begingroup \ $
@BillBarry感谢您的反馈,我很高兴看到自己的想法步入正轨。我从来没有亲自在JS中进行过这样的编程,因此,在我可能收到无响应的警告并开始挖掘之前,关于setTimeout的输入将永远不会进入我的脑海。
\ $ \ endgroup \ $
–克里斯·马里西奇(Chris Marisic)
2012年5月2日在18:02
#3 楼
从外观上最大的改变就是您的编程风格/质量:-函数将代码分解为更多有用的功能。该代码的很多尴尬之处是因为您尚未执行此操作。
寻找重复的代码/结构相同但略有不同的代码,然后看看如何将其包装到小功能。
用编程术语来说,这是一个称为透视级别的概念。有3个级别,即概念性/规范/实现(我在此处写了一些相关内容http://designingcode.blogspot.com/2006/12/its-matter-of-perspective.html)
基本上您将规范与实现混在一起,这会使代码有些混乱。
so
在规范中代码可能看起来像这样(也许):-
if( pitch == pitches.BALL) // or more probably as part of a switch/case
{
if( this.reachedBallLimit() )
{
this.walk();
}
}
#4 楼
请记住,这篇文章已有七年历史了,我应该花些时间来代替僵尸,而您可能从那时起就已经学到了很多关于JavaScript的知识(编辑:看来您在JS上有金牌因此,看来您已经学到了很多东西),我觉得应该提到的东西还没有..为了后代。如本文所述,方法应在原型上声明,而不是在构造函数中声明。这样,内存使用量将大大减少。
例如
Player
的构造函数-可以简化为以下内容,将内部声明的变量设置在this
上。var Player = function(pitcher, name) {
this.name = (name == undefined) ? "Nothing" : name;
this.balls = 0;
this.strikes = 0;
}
然后在原型上添加方法:
Player.prototype.getName = function() {
return this.name;
}
Player.prototype.atBat = function() {
...
虽然只有两个
Player
对象,但是这样做以减少内存消耗仍然是有益的。 br />
评论
我很奇怪,我的建筑师想说的是,选择使用零框架正在最大程度地重塑轮子,而您却无法利用经过500个设计阶段的锤子来钉钉子,因为您拥有锤子那只是建立在几个阶段。但是我的另一面看到了达到最高性能的价值,因为这是我们正在谈论的游戏。击球员的唯一出击方法就是击球吗?就此而言,我没有看到任何突破,飞出或双重比赛。我不是一个JavaScript的人,但是这个周末我在python中写了类似的东西。 bitbucket.org/jgrigonis/baseball_simulator/overview我对您的了解不多,到目前为止,我只拥有处理游戏状态更改事件的游戏类。
做得好。只是一件事...如果主队(列出的第二队)排在第9位,则该队不会命中。他们已经赢了。