轨道
/
Crystal
Crystal
/
练习
/
国际象棋
国际象棋

国际象棋

学习练习

简介

范围表示两个值之间的一段区间。 最常见的支持范围的类型是Int、Char和String。 它们有很多用途,比如快速创建集合、截取字符串、检查某个值是否在范围内,以及迭代。 范围用范围运算符..或...创建(前者包含结尾值,后者不包含)。

1..5  # A range containing 1, 2, 3, 4, 5
1...5 # A range containing 1, 2, 3, 4

之所以有两个范围运算符,是为了能创建包含或不包含结尾值的范围。比如在处理从 0 开始的下标时,这就很有用。

范围也可以用Range初始化器创建。

Range.new(1, 5) # A range containing 1, 2, 3, 4, 5
Note

在 Crystal 中用范围运算符..或...创建范围后,如果想在范围上调用方法,需要把范围用括号括起来。 否则,方法会被作用在范围运算符的第二个参数上。

(1..5).size # => 5
1..5.size # => Error: undefined method 'size' for Int32

获取子字符串

想截取字符串时,可以用范围运算符获取子字符串,也就是用子字符串的起始下标和结束下标创建一个范围。

"Hello World"[0..4] # => "Hello"
"Hello World"[6..10] # => "World"

也可以用负下标,从字符串末尾开始截取子字符串。

"Hello World"[-5..-1] # => "World"
"Hello World"[6..-4] # => "Wo"

范围方法

范围自带一组可以操作它们的方法。 例如,这些方法可以求范围中所有值之和,或者检查范围是否包含某个值。

方法 描述 示例
sum 返回范围内所有值之和 (1..5).sum # => 15
size 返回范围的大小 (1..5).size # => 5
includes? 如果范围包含给定的值则返回 true,否则返回 false (1..5).includes?(3) # => true

无开头和无结尾的范围

范围可以没有结尾,也可以没有开头。 无结尾或无开头的范围,其结尾值或开头值就是nil,但在定义范围时可以省略nil。

当你想从字符串开头开始截取,或者一直截取到结尾时,无开头或无结尾的范围就很有用。

"Hello World"[0..] # => "Hello World"
"Hello World"[4..] # => "o World"
"Hello World"[..5] # => "Hello"
Caution

如果不用在集合上,无结尾的范围可能会产生无限的序列,使用时务必小心。

Char 范围

Char 也可以用在范围里,让你得到两个 Char 之间的一段字符。 比如,想得到字母表时就很有用。

'a'..'z' # A range containing ['a', 'b', 'c', ..., 'z']

String 范围

String 也可以用在范围里,从而得到两个字符串之间的一段字符串。 不过当字符串范围里包含多个字符时,它的行为与Char略有不同。 字符串范围如果更复杂,它的行为可能会让人困惑,所以要小心使用。

("aa".."az") # A range containing ["aa", "ab", "ac", ..., "az"]

说明

作为一名国际象棋爱好者,你想写出自己的版本。没错,网上可能已经有很多现成的国际象棋实现,但你的独一无二!

首先,你要为棋子实现一套基本的移动系统。

这盘棋将在一个 8 格宽、8 格长的棋盘上进行。每个格子用一个字母和一个数字来标识。

1. 定义行与列的范围

游戏需要存储棋盘的行。行是棋盘的横排,编号从 1 到 8。

游戏还需要存储棋盘的列。列是棋盘的纵列,用字母 A 到 H 标识。

定义Chess::RANKS和Chess::FILES常量,分别存储行和列的范围。

Chess::RANKS
# => 1..8

Chess::FILES
# => 'A'..'H'

2. 检查格子是否有效

游戏需要检查一个格子是否有效。如果格子的行和列都在行与列的范围内,这个格子就是有效的。

定义Chess.valid_square?方法,它接收rank和file两个实参:rank保存表示行的整数,file保存表示列的字符。如果行和列都在行与列的范围内,方法应返回true,否则返回false。

Chess.valid_square?(1, 'A')
# => true

3. 获取玩家的昵称

游戏需要获取玩家的昵称。昵称由玩家名字的前两个字符和姓氏的后两个字符组成。昵称应该大写。

定义Chess.nickname方法,它接收first_name和last_name两个实参,分别保存玩家名字和姓氏的字符串。方法应以大写字符串的形式返回玩家的昵称。

Chess.nickname("John", "Doe")
# => "JOOE"

4. 创建移动消息

游戏需要为一次移动创建一条消息,说明哪位玩家移动到了哪个格子。消息中要用到玩家的昵称和他们移动到的格子。游戏还必须检查格子的列和行是否都在列与行的范围内,从而判断这步移动是否有效。

如果移动有效,消息应该是:"{nickname} moved to {square}" 如果移动无效,消息应该是:"{nickname} attempted to move to {square}, but that is not a valid square"

定义Chess.move_message方法,它接收三个实参:first_name保存玩家名字的字符串,last_name保存玩家姓氏的字符串,square保存玩家移动到的格子的字符串。方法应以字符串的形式返回这步移动的消息。

Chess.move_message("John", "Doe", "A1")
# => "JOOE moved to A1"
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Crystal Exercism

准备好开始 国际象棋 了吗?

注册 Exercism,借助 26 个概念142 个练习 和真人导师指导,学习并掌握 Crystal,全部免费。