博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bash高级使用_通过构建扫雷来磨练高级Bash技能
阅读量:2526 次
发布时间:2019-05-11

本文共 13126 字,大约阅读时间需要 43 分钟。

bash高级使用

我不是教授编程的专家,但是当我想在某些方面变得更好时,我试图找到一种玩转它的方法。 例如,当我想精通shell脚本时,我决定通过在Bash中编写游戏版本进行练习。

如果您是经验丰富的Bash程序员,并且想在玩耍时磨练自己的技能,请继续在终端中编写自己的Minesweeper版本。 完整的源代码可在此找到。

做好准备

在开始编写任何代码之前,我概述了创建游戏所需的要素:

  1. 打印雷区
  2. 创建游戏逻辑
  3. 创建逻辑以确定可用的雷区
  4. 保持可用和发现(提取)地雷的数量
  5. 创建残局逻辑

在Minesweeper中,游戏世界是2D隐藏的单元阵列(列和行)。 每个单元可能包含也可能不包含爆炸地雷。 玩家的目标是揭示不包含地雷的单元,并且永远不显示地雷。 游戏的Bash版本使用10x10矩阵,该矩阵使用简单的bash数组实现。

首先,我分配一些随机变量。 这些是可以在板上放置地雷的位置。 通过限制位置数量,可以很容易地在此基础上进行构建。 逻辑可能会更好,但我想让游戏看起来简单而又不成熟。 (我写这个很有趣,但是很高兴欢迎您的贡献,使它看起来更好。)

以下变量是一些默认变量,它们声明为随机调用以进行字段放置,例如变量ag,我们将使用它们来计算可提取的地雷:

# variables      
score =
0
# will be used to store the score of the game
# variables below will be used to randomly get the extract-able cells/fields from our mine.
a =
"1 10 -10 -1"
b =
"-1 0 1"
c =
"0 1"
d =
"-1 0 1 -2 -3"
e =
"1 2 20 21 10 0 -10 -20 -23 -2 -1"
f =
"1 2 3 35 30 20 22 10 0 -10 -20 -25 -30 -35 -3 -2 -1"
g =
"1 4 6 9 10 15 20 25 30 -30 -24 -11 -10 -9 -8 -7"
#
# declarations
declare
-a room  
# declare an array room, it will represent each cell/field of our mine.

接下来,我用(0-9)列和(aj)行打印我的木板,形成一个10x10矩阵,用作游戏的雷区。 (M [10] [10]是一个带有索引0-99的100值数组。)如果您想了解有关Bash数组的更多信息,请阅读“ 。

让我们称它为函数,首先我们打印标题:两条空白行,列标题和概述比赛场地顶部的一行:

printf      
'\n\n'
printf
'%s'
"     a   b   c   d   e   f   g   h   i   j"
printf
'\n   %s\n'
"-----------------------------------------"

接下来,我建立一个计数器变量r ,以跟踪填充了多少水平行。 请注意,稍后在游戏代码中,我们将使用与数组索引相同的计数器变量' r '。 在 ,使用seq命令从0递增到9,我输出一个数字( d% )表示行号($ row,由seq定义):

r =      
0
# our counter
for row
in $
(
seq
0
9
) ;
do
 
printf
'%d  '
" $row "
# print the row numbers from 0-9

在我们从这里继续前进之前,让我们检查一下到目前为止所取得的成就。 我们首先水平打印序列[aj] ,然后在[0-9]范围内打印行号,我们将使用这两个范围作为用户输入坐标来定位要提取的地雷。  

下一个,   在每一行中,都有一个列交叉点,因此该打开一个新的for循环了。 这个负责管理每一列,因此它实质上是在运动场中生成每个单元。 我添加了一些帮助程序功能,您可以在源代码中看到的完整定义。 对于每个单元格,我们都需要一些东西来使该字段看起来像一个矿井,因此,我们使用一个名为的自定义函数,用一个点(。)来初始化空的。 另外,我们需要一个数组变量来存储每个单元格的值,我们将使用预定义的全局数组变量以及索引 。 随着r的增加,我们遍历单元格,一路掉下地雷。

       
for
col
in $
(
seq
0
9
) ;
do
   
(
( r+=
1
)
)  
# increment the counter as we move forward in column sequence
    is_null_field
$r  
# assume a function which will check, if the field is empty, if so, initialize it with a dot(.)
   
printf
'%s \e[33m%s\e[0m '
"|"
" ${room[$r]} "
# finally print the separator, note that, the first value of ${room[$r]} will be '.', as it is just initialized.
 
#close col loop
 
done

最后,通过用一行将每行的底部括起来,然后关闭行循环,使电路板保持良好的定义:

printf      
'%s\n'
"|"  
# print the line end separator
printf
'   %s\n'
"-----------------------------------------"
# close row for loop
done
printf
'\n\n'

完整的功能如下:

plough      
(
)
{
 
r =
0
 
printf
'\n\n'
 
printf
'%s'
"     a   b   c   d   e   f   g   h   i   j"
 
printf
'\n   %s\n'
"-----------------------------------------"
 
for row
in $
(
seq
0
9
) ;
do
   
printf
'%d  '
" $row "
   
for
col
in $
(
seq
0
9
) ;
do
       
(
( r+=
1
)
)
       is_null_field
$r
       
printf
'%s \e[33m%s\e[0m '
"|"
" ${room[$r]} "
   
done
   
printf
'%s\n'
"|"
   
printf
'   %s\n'
"-----------------------------------------"
 
done
 
printf
'\n\n'
}

我花了一些时间决定是否需要is_null_field ,所以让我们仔细看一下它的作用。 从游戏一开始,我们需要一个可靠的状态。 该选择是任意的-它可以是数字或任何字符。 我决定假定所有内容都声明为点(。),因为我相信它会使游戏板看起来更漂亮。 看起来像这样:

is_null_field      
(
)
{
 
local
e =
$1
# we used index 'r' for array room already, let's call it 'e'
   
if
[
[
-z
" ${room[$e]} "
]
] ;
then
      room
[
$r
] =
"."  
# this is where we put the dot(.) to initialize the cell/minefield
   
fi
}

现在,我已经初始化了我的地雷中的所有单元,通过声明并随后调用如下所示的简单函数,我得到了所有可用地雷的计数:

get_free_fields      
(
)
{
 
free_fields =
0    
# initialize the variable
 
for n
in $
(
seq
1
${#room[@]}
) ;
do
   
if
[
[
" ${room[$n]} " =
"."
]
] ;
then  
# check if the cells has initial value dot(.), then count it as a free field.
     
(
( free_fields+=
1
)
)
   
fi
 
done
}

这是打印的雷区,其中[ aj]是列,[ 0-9 ]是行。

Minefield

创建驱动播放器的逻辑

播放器逻辑从读取一个选项作为地雷的坐标,并提取雷场上的精确区域。 它使用Bash的提取列和行输入,然后将列馈入一个开关,该开关指向板上等效的整数符号,要了解这一点,请参阅在switch case语句中将值分配给变量' o'下面。 例如,玩家可能输入c3 ,Bash将其分为两个字符: c3 。 为了简单起见,我跳过了无效条目的处理方式。

       
colm =
${opt:0:1}  
# get the first char, the alphabet
 
ro =
${opt:1:1}    
# get the second char, the digit
 
case
$colm
in
    a
)
o =
1
;;      
# finally, convert the alphabet to its equivalent integer notation.
    b
)
o =
2
;;
    c
)
o =
3
;;
    d
)
o =
4
;;
    e
)
o =
5
;;
    f
)
o =
6
;;
    g
)
o =
7
;;
    h
)
o =
8
;;
    i
)
o =
9
;;
    j
)
o =
10
;;
 
esac

然后,它计算出精确的索引,并将输入坐标的索引分配给该字段。

shuf命令在这里也有很多用途, shuf是一种旨在提供信息的随机排列,其中-i选项表示改编的索引或可能范围,而-n表示返回的最大数量或输出。 双括号允许在Bash中进行 ,我们将在此处大量使用它们。

假设前面的示例通过stdin接收到c3 。 然后,从上述switch case语句中ro = 3o = 3c转换为其等效整数,并将其放入我们的公式中以计算最终索引' i'。

       
i =$
(
(
( ro
*
10
) +o
)
)  
# Follow BODMAS rule, to calculate final index.
  is_free_field
$i $
( shuf
-i
0 -
5
-n
1
)  
# call a custom function that checks if the final index value points to a an empty/free cell/field.

通过此数学运算来了解如何计算最终索引“ i ”:

i =$      
(
(
( ro
*
10
) +o
)
)
i =$
(
(
(
3
*
10
) +
3
)
) =$
(
(
30 +
3
)
) =
33

最终索引值为33。在上面印刷的板上,最终索引指向第33个单元格,该索引应为第3行(从0开始,否则为第4行)和第3(C)列。

创建逻辑以确定可用的雷区

为了提取地雷,在对坐标进行解码并找到索引之后,程序将检查该字段是否可用。 如果不是,程序将显示警告,然后玩家选择另一个坐标。

在此代码中,如果单元格包含点( )字符,则该单元格可用。 假设可用,将重置单元格中的值并更新分数。 如果一个单元格由于不包含点而不可用,则设置一个变量not_allowed 。 为简便起见,我留给您看一下游戏源代码,以了解游戏逻辑中的内容。

is_free_field      
(
)
{
 
local
f =
$1
 
local
val =
$2
 
not_allowed =
0
 
if
[
[
" ${room[$f]} " =
"."
]
] ;
then
    room
[
$f
] =
$val
   
score =$
(
( score+val
)
)
 
else
   
not_allowed =
1
 
fi
}
Extracting mines

如果输入的坐标可用,则发现地雷,如下所示。 当提供h6作为输入时,一些值随机填充在我们的雷区中,这些值会在提取分钟后添加到用户分数中。

Extracting mines

现在记住我们在开始时声明的变量[ag],我现在将在这里使用它们来提取随机矿井,并使用Bash间接将其值分配给变量m 。 这样,根据输入的坐标,程序选取一个随机组附加号码(M)来计算附加的字段将被填充的(如上所示)通过将其添加到原始的输入坐标,来表示这里以i(如上计算

请注意,下面的代码片段中的字符X是我们唯一的GAME-OVER触发器,我们将其添加到我们的随机播放列表中以随机出现,带有shuf命令的妙处 ,它可以在任意多次出现之后出现,甚至可能不会出现给我们幸运的获奖用户。

m =$      
( shuf
-e a b c d e f g X
-n
1
)  
# add an extra char X to the shuffle, when m=X, its GAMEOVER
 
if
[
[
" $m "
! =
"X"
]
] ;
then        
# X will be our explosive mine(GAME-OVER) trigger
   
for limit
in
${!m} ;
do          
# !m represents the value of value of m
     
field =$
( shuf
-i
0 -
5
-n
1
)    
# again get a random number and
     
index =$
(
( i+limit
)
)            
# add values of m to our index and calculate a new index till m reaches its last element.
      is_free_field
$index
$field
   
done

我希望所有显示的单元格都与玩家选择的单元格相邻。

Extracting mines

保持可用和已开采地雷的数量

该计划需要跟踪雷区中可用的牢房; 否则,即使在所有单元格都显示出来之后,它仍会继续询问玩家输入。 为了实现这一点,我创建了一个名为free_fields的变量,最初将其设置为0。在一个for循环中,该循环由我们的雷区中剩余的可用单元格/字段数定义。 如果单元格包含点( ),则free_fields的计数增加。

get_free_fields      
(
)
{
 
free_fields =
0
 
for n
in $
(
seq
1
${#room[@]}
) ;
do
   
if
[
[
" ${room[$n]} " =
"."
]
] ;
then
     
(
( free_fields+=
1
)
)
   
fi
 
done
}

等待,如果free_fields = 0怎么办? 这意味着,我们的用户已经提取了所有地雷。 请随时查看以更好地理解。

if      
[
[
$free_fields
-eq
0
]
] ;
then  
# well that means you extracted all the mines.
     
printf
'\n\n\t%s: %s %d\n\n'
"You Win"
"you scored"
" $score "
     
exit
0
fi

创建Gameover的逻辑

对于Gameover情况,我们使用一些将其打印到终端的中间,我将其留给读者来研究其工作原理。

if      
[
[
" $m " =
"X"
]
] ;
then
   
g =
0                      
# to use it in parameter expansion
    room
[
$i
] =X              
# override the index and print X
   
for j
in
{
42 ..
49
} ;
do    
# in the middle of the minefields,
     
out =
"gameover"
     
k =
${out:$g:1}          
# print one alphabet in each cell
      room
[
$j
] =
${k^^}
     
(
( g+=
1
)
)
   
done
fi

最后,我们可以打印最期待的两行。

if      
[
[
" $m " =
"X"
]
] ;
then
     
printf
'\n\n\t%s: %s %d\n'
"GAMEOVER"
"you scored"
" $score "
     
printf
'\n\n\t%s\n\n'
"You were just $free_fields mines away."
     
exit
0
fi
Minecraft Gameover

就是这样,伙计们! 如果您想了解更多信息,请从我的访问此Minesweeper游戏和Bash中其他游戏的源代码。 我希望它能给您一些启发,让他们了解更多Bash并在此过程中获得乐趣。

翻译自:

bash高级使用

转载地址:http://cvszd.baihongyu.com/

你可能感兴趣的文章
jQueru中数据交换格式XML和JSON对比
查看>>
form表单序列化后的数据转json对象
查看>>
[PYTHON]一个简单的单元測试框架
查看>>
iOS开发网络篇—XML数据的解析
查看>>
[BZOJ4303]数列
查看>>
数据库TCPIP协议开了,但还是远程连不上
查看>>
一般处理程序在VS2012中打开问题
查看>>
C语言中的++和--
查看>>
thinkphp3.2.3入口文件详解
查看>>
POJ 1141 Brackets Sequence
查看>>
Ubuntu 18.04 root 使用ssh密钥远程登陆
查看>>
linux 单机跨进程通信
查看>>
Servlet和JSP的异同。
查看>>
虚拟机centOs Linux与Windows之间的文件传输
查看>>
ethereum(以太坊)(二)--合约中属性和行为的访问权限
查看>>
IOS内存管理
查看>>
监听系统键盘的方法
查看>>
Mysql两个引擎对比
查看>>
测试书籍推荐
查看>>
php魔术方法
查看>>