热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

实用的CoffeeScript:制作井字游戏

CoffeeScript是一种很小的小语言,可以编译为JavaScript。由于您编写CoffeeScript,将其编译为JavaScript并将生成的

CoffeeScript是一种很小的小语言,可以编译为Javascript。 由于您编写CoffeeScript,将其编译为Javascript并将生成的Javascript文件用于您的应用程序,因此在运行时没有解释。 您可以在CoffeeScript中使用任何Javascript库(例如jQuery),只需将其功能与适当的CoffeeScript语法结合使用即可。 CoffeeScript既可以用于在前端编写Javascript,也可以在后端编写Javascript。

那么为什么要使用CoffeeScript?

更少的代码

根据有关CoffeeScript的小书 ,CoffeeScript的语法减少了使JS工作所需键入的字符数,大约减少了33%至50%。 我将展示一个使用CoffeeScript创建的简单井字游戏(您可能已经从标题中猜到了),其原始CoffeeScript格式包含4963个字符,而已编译的Javascript代码包含7669个字符。 那相差2706个字符或36%!

更快的开发时间

因为您编写的代码更短,更不容易出错(例如,变量是自动作用域的,这意味着您不会通过忽略var意外覆盖全局变量),所以可以更快地完成项目。 CoffeeScript简洁的语法还使代码更具可读性,最终使代码更易于维护。

入门

在本文中,我们将使用CoffeeScript和jQuery构建一个简单的井字游戏。 如果您想在研究实际案例之前先阅读语法,建议在SitePoint上发表我的使用CoffeeScript加速Javascript开发文章。 这也详细说明了如何通过npm(节点程序包管理器)安装CoffeeScript。

与以往一样,本教程中的所有代码都可以在GitHub上获得,而演示则可以在CodePen或教程的末尾获得 。

您将使用的最常见的CoffeeScript命令是:

coffee -c fileName会将CoffeeScript文件编译为具有相同名称但扩展名为.js文件(CoffeeScript文件通常具有.coffee扩展名)。

coffee -cw fileName将监视文件中的更改(无论何时保存文件)并进行编译。

coffee -cw folderName/将监视文件夹中所有.coffee文件的更改,并在发生任何更改时将它们编译在同一目录中。

最后,将CoffeeScript从具有.coffee文件的文件夹编译为仅包含.js文件的文件夹很方便。

coffee -o js/ -cw /coffee将监视coffee文件夹中所有.coffee文件中的更改,并将输出(Javascript)放在js文件夹中。

如果您不熟悉终端,则可以使用带有GUI的工具来处理CoffeeScript文件。 例如,您可以免费试用无限制的Propros (尽管您可以根据需要购买)。 下图显示了它提供的一些选项:

Prepros的屏幕截图

您可以看到Prepros为您完成了所有工作-它设置了观察程序,因此您的.coffee文件将被编译为JS,它使您可以使用Uglify JS来缩小/压缩代码,它可以自动处理变量并支持冰咖啡脚本 。 Prepros还可以用于CSS预处理器(例如Less和Sass)和模板引擎(例如Jade)。

游戏

让我们从标记开始:

Tic Tac Toe

...

游戏的界面包括以下内容:

  • 标题,简要描述游戏
  • 一个div元素,其id为board ,即3×3正方形所在的位置
  • 带有一类alerts div元素,将在其中显示游戏状态
  • 带有一类notifications div元素,它将显示谁在玩X和O,以及常规玩家统计信息。
  • 仅在游戏加载时显示的表格,并提示玩家输入他们的姓名。

根据最佳实践,jQuery和使我们的应用程序滴答作响的脚本都在结束body标记之前加载。

造型

使用CSS,通过浮动每个正方形并清除第四个正方形,可以使涉及的九个正方形出现在3×3网格中。

.square:nth-of-type(3n + 1) {clear: both;
}

我们还可以根据正方形具有xo类(使用Javascript添加)来为正方形添加不同的颜色。

.square.x {color: crimson;
}.square.o {color: #3997ff;
}

行动中的CoffeeScript

作为参考,您可以在此处找到主要的CoffeeScript文件。

您可以看到我们的Tic-Tac-Toe应用程序以$ ->开头,这等效于DOM准备就绪时执行代码的jQuery函数的简写: $(function() { ... });

CoffeeScript不依靠分号和花括号,而是依靠缩进。 ->告诉CoffeeScript您正在定义一个函数,因此您可以在下一行开始该函数的主体并将主体缩进两个空格。

接下来,我们创建一个名为Tic的对象,该对象本身包含一个名为data的对象。 您可以看到,只要正确缩进属性,在创建对象时就不必使用花括号或逗号。

$ ->Tic =data:turns: 0x: {}o: {}gameOver: false

turns属性将保存游戏中的总回合数。 我们可以检查它是否为偶数或不均匀数,并以此方式确定是X还是O的转弯。

xo属性是对象,将包含与对游戏很重要的三个轴上的X或O的数量有关的数据:水平,垂直和对角线。 它们将通过checkEnd方法在每次移动时进行更新,以表示X和O在板上的分布。 然后, checkEnd方法将调用checkWin以确定是否有赢家。

之后,我们在Tic对象中有了一个方法,该方法可以使一切正常运行:

initialize: ->@data.gameOver = false@.setPlayerNames()@.retrieveStats()@.assignRoles()@.prepareBoard()@.updateNotifications()@.addListeners()

请注意@的使用,它将编译为Javascript关键字this 。 如initialize的第一个属性所示,在设置或调用属性或方法时,可以跳过@关键字后的点。

通过给这些方法取一个合理的名称,我们对它们的工作有一个清晰的认识:

  • setPlayerNames将用户在输入中输入的值存储到data对象中。
  • retrieveStats检索从localStorage的和集合起来的球员的统计data对象。
  • assignRoles确定谁在玩X,谁在玩O。
  • prepareBoard隐藏该表单,删除所有通知,清空该板,并用九个空白方块填充它。
  • updateNotifications使用有关谁在玩X和谁在玩O以及玩家的统计信息updateNotifications更新UI。
  • addListeners附加事件侦听器,以便我们可以响应玩家的移动。

深潜

让我们更详细地研究其中的两种方法。

prepareBoard: ->...$("

", {class: "square"}).appendTo("#board") for square in [0..8]

在这里,我们进行了九次迭代,并将九个带有一类square div添加到空板上以填充它。 这演示了CoffeeScript如何让您编写单行循环并在编写条件本身之前声明循环的主体。

updateNotifications: ->$(".notifications").empty().show()@.addNotification "#{@data.player1} is playing #{@data.rolep1}"...

CoffeeScript允许进行字符串内插,从而增加了可读性并减少了复杂性和代码长度。 您可以在任何字符串中添加#{} ,并在括号内插入任何变量或函数调用的返回值。

addNotification: (msg) ->$(".notifications").append($("

", text: msg));

addNotification方法示例了如何在CoffeeScript中定义参数。 您将它们写在箭头( -> )之前:

您可以为类似于PHP的参数提供默认值:

addNotification: (msg = "I am a message") ->

编译具有默认参数的函数时,它将转换为:

if (msg == null) { msg = "I am a message"; }

最后,让我们转到addListeners方法:

addListeners: ->$(".square").click ->if Tic.data.gameOver is no and not $(@).text().lengthif Tic.data.turns % 2 is 0 then $(@).html("X").addClass("x moved")else if Tic.data.turns % 2 isnt 0 then $(@).html("O").addClass("o moved")...

在这里,我们看到CoffeeScript提供了其他关键字来表示真实值和虚假值,例如no , yes , offon 。 此外, !== , === , && , ! 可以使用isnt , is andnot相应地表示。

您可以使用if ... then ... else ...语法制作可读的单行条件。

游戏机制

主力方法checkEnd每次玩家移动时检查是否有赢家。 它通过在板上迭代并计算属于X和O的正方形来实现此目的。它首先检查对角轴,然后是垂直轴,然后是水平轴。

checkEnd : ->@.data.x = {}@.data.o = {}#diagonal checkdiagonals = [[0,4,8], [2,4,6]]for diagonal in diagonalsfor col in diagonal@.checkField(col, 'diagonal')@.checkWin()@.emptyStorageVar('diagonal')for row in [0..2]start = row * 3end = (row * 3) + 2middle = (row * 3) + 1#vertical check@checkField(start, 'start')@checkField(middle, 'middle')@checkField(end, 'end')@checkWin()#horizontal checkfor column in [start..end]@checkField(column, 'horizontal')@checkWin()@emptyStorageVar('horizontal')

如您所见,这利用了另一个便捷的CoffeeScript功能-范围。

for row in [0..2]

这将循环三次,按此顺序将行设置为0、1和2。 或者, [0...2] (互斥范围)将仅导致两次迭代,将行设置为0和1。

在水平检查中,我们再次看到缩进在确定什么是循环的一部分以及什么在循环的外部是至关重要的,只有checkField调用在内部循环内部。

这是checkField样子:

checkField: (field, storageVar) ->if $(".square").eq(field).hasClass("x")if @.data.x[storageVar]? then @.data.x[storageVar]++ else @.data.x[storageVar] = 1else if $(".square").eq(field).hasClass("o")if @.data.o[storageVar]? then @.data.o[storageVar]++ else @.data.o[storageVar] = 1

此方法演示了?的用法? 关键字,当插入到条件变量中时,可编译为:

if (typeof someVariable !== "undefined" && someVariable !== null) {

这显然很方便。

checkField方法的作用是,根据单击的正方形的类名称,在xo属性的相应轴上添加一个。 当用户在addListeners方法中单击空白的addListeners正方形时,将添加类名称。

这使我们进入checkWin方法,该方法用于检查其中一名玩家是否赢得了比赛:

checkWin: ->for key,value of @.data.xif value >= 3localStorage.x++@showAlert "#{@.getPlayerName("X")} wins"@data.gameOver = true@addToScore("X")for key,value of @.data.oif value >= 3localStorage.o++@showAlert "#{@.getPlayerName("O")} wins"@data.gameOver = true@addToScore("O")

在CoffeeScript中,您可以使用for ... in array来遍历数组值,并for key,value of object遍历for key,value of object的属性。 checkWin利用它来检查xo对象内部的所有属性。 如果其中任何一个的数字大于或等于3,那么我们就有赢家,比赛应该结束。 在这种情况下,我们调用addToScore方法,该方法通过localStorage保留播放器的结果。

关于本地存储的一句话

LocalStorage是Web Storage规范的一部分,具有很好的浏览器支持 。 它允许您将数据(类似于COOKIE)存储在用户的计算机上,并在需要时随时访问。

您可以通过多种方式访问​​API,例如,就像访问常规对象的属性一样:

//fetch item
localStorage.myProperty // set item
localStorage.myProperty = 123

本地存储总是保存字符串,因此,如果要存储对象或数组, JSON.stringify在存储数组/对象时必须使用JSON.stringify ,而在检索数组/对象时必须使用JSON.parse

我们的addToScore方法利用了以下事实:

addToScore: (winningParty) ->...if winningParty is "none"@.showAlert "The game was a tie"else...localStorage[@data.player1] = JSON.stringify @data.p1stats

它还说明了如何在CoffeeScript( JSON.stringify )中省略括号,尽管仅建议最JSON.stringify的函数调用使用。

接下来,我们有两种实用方法。 我们使用emptyStorageVar清除特定水平行或对角线的内容。 这是必要的,因为板上有两个对角线,并且在我们的chekEnd方法内部,我们对两个对角线使用相同的data属性。 因此,我们必须在检查第二个对角线之前清除该属性。 水平行也是如此。

emptyStorageVar: (storageVar) ->@.data.x[storageVar] = null@.data.o[storageVar] = null

获取玩家名称

在游戏开始时提交带有球员姓名的表格时,我们可以阻止其默认操作并使用Javascript处理提交。 我们检查名称是否为空或两个名称是否相同,如果相同,则显示友好的警报。 否则,我们通过调用Tic.initialize()开始游戏。

$("form").on "submit", (evt) ->evt.preventDefault()$inputs = $("input[type='text']")namesNotEntered = $inputs.filter(->return @.value.trim() isnt "").length isnt 2namesIndentical = $inputs[0].value is $inputs[1].valueif namesNotEntered then Tic.showAlert("Player names cannot be empty")else if namesIndentical then Tic.showAlert("Player names cannot be identical")else Tic.initialize()

最后一行使用事件委托使类play-again任何元素响应单击。 必须进行事件委托,因为只有在游戏结束后才将此元素添加到页面。 首次渲染DOM时不存在。

$("body").on("click", ".play-again", -> Tic.initialize())

全部放在一起

就是这样。 在少于150行的CoffeeScript中,我们有一个有效的游戏。 别忘了,您可以从GitHub下载本教程中的代码。

请参阅CodePen上的SitePoint ( @SitePoint )的笔井字游戏 。

结论

我希望本教程能够巩固您对CoffeeScript的了解,并向您展示jQuery和CoffeeScript如何协同工作。 您可以做很多事情来改善游戏。 例如,您可以添加一个选项以使电路板不同于其标准的3×3尺寸。 您可以实现一些简单的AI,以便玩家可以在机器上玩游戏,也可以在游戏中实现炸弹,例如,在玩家为荣耀而战时,在随机的游戏动作中添加随机的X或O。

From: https://www.sitepoint.com/practical-coffeescript-tic-tac-toe/



推荐阅读
author-avatar
律师苏俊杰
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有