【算法】游戏中的学习,使用c#面向对象特性控制游戏角色移动

算法,游戏,学习,使用,c#,面向对象,特性,控制,角色,移动 · 浏览次数 : 123

小编点评

```csharp public class PlayerMovement { public int x; public int y; public PlayerMovement(int x, int y) { this.x = x; this.y = y; } public void Press(Direction dir) { Console.WriteLine("Pressed " + dir); Input.Press(dir); } public void Release(Direction dir) { Console.WriteLine("Released " + dir); Input.Release(dir); } } public enum Direction { Down, Left, Right, Up } ``` **使用方法:** 1. 创建一个 `PlayerMovement` 对象,指定 `x` 和 `y` 的位置。 2. 使用 `Press()` 和 `Release()` 方法,指定方向的键。 3. 使用 `Input.Get()` 获取键盘的键。 4. 在 `RandomTest()` 方法中,使用 `Press()` 和 `Release()` 方法,模拟随机的键 press 和释放。 **示例:** ``` // 创建 PlayerMovement 对象 PlayerMovement player = new PlayerMovement(100, 100); // 按下 Down 键 player.Press(Direction.Down); // 释放 Down 键 player.Release(Direction.Down); // 使用 Input.Get 获取键盘的键 Console.WriteLine(player.x); // 100 ```

正文

最近,小悦的生活像是一首繁忙的交响曲,每天忙得团团转,虽然她的日程安排得满满当当,但她并未感到充实。相反,她很少有时间陪伴家人,这让她感到有些遗憾。在周五的午后,小悦的哥哥突然打来电话,他的声音里充满了焦虑。

“小悦,我有个事情想拜托你。”哥哥的声音传来。

小悦不禁有些疑惑,哥哥有什么事情需要她帮忙呢?她忍不住问:“哥哥,有什么需要我帮忙的吗?”

哥哥解释说:“我最近要出差一段时间,大概一个星期左右。而我的妻子目前正在照顾住院的父母,没有时间照顾小明。我想请你帮忙照顾小明一段时间。”

小悦愣住了,没想到哥哥会把这个重任托付给她。这是一个不小的挑战,她有些担心自己无法胜任。但是,她知道她不能让自己的感情影响到哥哥的决定。于是她坚定地回答:“好的,哥哥,我会照顾好小明的。你放心去出差吧。”

电话那头的哥哥松了一口气,感激地说:“谢谢你,小悦。我相信你会照顾好小明的。我会尽快回来的。”

就这样,小悦接下了照顾小明的重任。虽然她很忙,但是她觉得为了哥哥和小明,她要付出更多的努力和时间。接下来的几天,小悦事无巨细地照顾着小明,给他做饭、洗衣、辅导作业,尽职尽责地扮演着一个姑姑的角色。

在周末的下午,小悦坐在电脑前,专注地处理着工作。她穿着舒适的衣裤,一头长发随意扎起,给人一种亲切自然的感觉。突然,门口传来了敲门声,小悦放下手中的工作,打开门一看,原来是她的侄儿小明。

小明是个活泼好动的孩子,二年级的他还保留着童年的纯真和好奇。他瞪大了眼睛,好奇地看着小悦的电脑屏幕:“姐姐,你在干什么呀?”小悦微微一笑,纠正道:“我是姑姑,不是姐姐。”小明尴尬地挠了挠头,赶紧改口:“姑姑,你在干什么呀?”

小悦笑着回答:“我在写程序呀。”

“程序?可以教我吗?”小明充满期待地问。

“当然可以,”小悦点点头,她向小明介绍了一下编程的基本知识和规则,然后开始手把手地教他如何编写简单的游戏程序。

小悦耐心地教导小明如何设计坦克模拟游戏。她先向他介绍了游戏的基本规则,然后手把手地教他如何用程序操作键盘上的十字键来控制屏幕中的坦克移动。在这个过程中,小悦始终保持着微笑,她温柔的话语和细心的指导让小明倍感亲切。

时间在不知不觉中流逝,小悦的心里也渐渐感到了疲惫。但每当看到小明在编程的世界中自由翱翔的时候,她的心里就充满了欣慰和自豪。小明也渐渐地开始明白了编程的奥秘,他的兴趣被极大地激发了出来,每天都充满了新的期待和挑战。尽管这段日子很累很忙,但小悦觉得很有意义。因为她知道这段时间对小明来说是宝贵的,而她也有幸参与其中,这让她感到无比的满足和幸福。


 小悦使用面向对象的思想,设计了一个名为PlayerMovement的类,用于实时控制游戏中玩家的移动。该类包含一个directions列表,用于存储当前按下的方向键。Position属性表示当前玩家的位置,Direction属性表示当前玩家的方向。

算法实现1:

 1  public enum Direction { Up = 8, Down = 2, Left = 4, Right = 6 }
 2   
 3   public struct Tile
 4   {
 5       public int X { get; }
 6       public int Y { get; }
 7       
 8       public Tile(int x, int y);
 9   }
10   
11   public static class Input
12   {
13       // pressed = true, released = false
14       public static bool GetState(Direction direction);
15   }
 1 public class PlayerMovement
 2 {
 3     private List<Direction> directions = new List<Direction>();
 4 
 5     public Tile Position { get; private set; }
 6     public Direction Direction { get; private set; }
 7 
 8     public PlayerMovement(int x, int y)
 9     {
10         // 初始化玩家位置
11         this.Position = new Tile(x, y);
12     }
13 
14     public void Update()
15     {
16         // 获取新的方向
17         this.directions = GetNewDirections(this.directions);
18         if (!this.directions.Any()) return;
19 
20         var currentDirection = this.directions.Last();
21         if (this.Direction != currentDirection)
22         {
23             // 更新玩家方向
24             this.Direction = currentDirection;
25         }
26         else
27         {
28             // 获取新的位置
29             this.Position = GetNewPosition(this.Position, this.Direction);
30         }
31     }
32 
33     private static List<Direction> GetNewDirections(IEnumerable<Direction> current)
34     {
35         var newState = current.ToList();
36 
37         var currentDirections = GetCurrentDirections().ToList();
38 
39         var directionsToRemove = newState.Except(currentDirections).ToList();
40         directionsToRemove.ForEach(d => newState.Remove(d));
41 
42         var directionsToAdd = currentDirections.Except(newState).ToList();
43         directionsToAdd.ForEach(d => newState.Add(d));
44 
45         return newState;
46     }
47 
48     private static Tile GetNewPosition(Tile pos, Direction dir)
49     {
50         var newX = pos.X;
51         var newY = pos.Y;
52 
53         switch (dir)
54         {
55             case Direction.Left:
56                 newX--;
57                 break;
58             case Direction.Right:
59                 newX++;
60                 break;
61             case Direction.Down:
62                 newY--;
63                 break;
64             case Direction.Up:
65                 newY++;
66                 break;
67         }
68 
69         return new Tile(newX, newY);
70     }
71 
72     private static IEnumerable<Direction> GetCurrentDirections()
73     {
74         // 通过yield return获取当前按键状态并返回对应的方向
75         if (Input.GetState(Direction.Right)) yield return Direction.Right;
76         if (Input.GetState(Direction.Left)) yield return Direction.Left;
77         if (Input.GetState(Direction.Down)) yield return Direction.Down;
78         if (Input.GetState(Direction.Up)) yield return Direction.Up;
79     }
80 }

通过测试用例解释运行过程:

 1       private PlayerMovement _player;
 2   
 3       private void TestEquality(Direction direction, int x, int y)
 4       {
 5           _player.Update();
 6   
 7           Assert.AreEqual(direction, _player.Direction);
 8           Assert.AreEqual(new Tile(x, y), _player.Position);
 9       }
10   
11       [Test(Description = "Basic Test 1")]
12       public void BasicTest1()
13       {
14           _player = new PlayerMovement(0, 0);
15           Input.Clear();
16 
17           Press(Direction.Down);
18 
19           TestEquality(Direction.Down, 0, 0);
20           TestEquality(Direction.Down, 0, -1);
21           TestEquality(Direction.Down, 0, -2);
22 
23           Press(Direction.Left);
24           Press(Direction.Right);
25 
26           TestEquality(Direction.Left, 0, -2);
27           TestEquality(Direction.Left, -1, -2);
28 
29           Release(Direction.Left);
30 
31           TestEquality(Direction.Right, -1, -2);
32 
33           Release(Direction.Right);
34 
35           TestEquality(Direction.Down, -1, -2);
36           TestEquality(Direction.Down, -1, -3);
37 
38           Release(Direction.Down);
39 
40           TestEquality(Direction.Down, -1, -3);
41       }

首先,我们创建了一个名为_playerPlayerMovement对象,并将其初始位置设置为(0, 0)。然后,我们清除了输入状态。

接下来,我们按下了向下方向键。这将触发Input.GetState(Direction.Down)返回true,所以GetCurrentDirections函数将返回一个包含Direction.Down的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表只包含一个元素Direction.Down,所以它不会发生任何变化。

TestEquality方法中,我们首先调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(0, -1)。然后,我们使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

接下来,我们再次调用_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(0, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

然后,我们按下了向左和向右方向键。这将触发Input.GetState(Direction.Left)Input.GetState(Direction.Right)都返回true,所以GetCurrentDirections函数将返回一个包含Direction.LeftDirection.Right的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表已经包含了Direction.Down,所以它将保持不变。

TestEquality方法中,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(0, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

然后,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Left,我们期望玩家的位置为(-1, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

接下来,我们释放了向左方向键。这将触发Input.GetState(Direction.Left)返回false,所以GetCurrentDirections函数将返回一个不包含Direction.Left的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表已经包含了Direction.DownDirection.Right,所以它将保持不变。

TestEquality方法中,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Right,我们期望玩家的位置为(-1, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

然后,我们释放了向右方向键。这将触发Input.GetState(Direction.Right)返回false,所以GetCurrentDirections函数将返回一个不包含Direction.Right的集合。然后,GetNewDirections函数将检查当前方向列表directions和新的方向集合之间的差异,并更新directions列表。由于directions列表已经包含了Direction.Down,所以它将保持不变。

TestEquality方法中,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(-1, -2)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。

最后,我们再次调用了_player.Update()来更新玩家的状态。根据当前方向Direction.Down,我们期望玩家的位置为(-1, -3)。我们再次使用Assert.AreEqual方法来检查玩家的方向和位置是否与预期相符。


算法实现2:

 1 public class PlayerMovement
 2 {
 3     public Tile Position { get; private set; }  // 玩家的位置
 4     public Direction Direction { get; private set; }  // 玩家的方向
 5     
 6     // 不同方向对应的坐标变化
 7     private Dictionary<Direction, (int xDelta, int yDelta)> moves = new Dictionary<Direction, (int xDelta, int yDelta)>()
 8     {
 9         { Direction.Up, (0, 1) },
10         { Direction.Down, (0, -1) },
11         { Direction.Left, (-1, 0) },
12         { Direction.Right, (1, 0) }
13     };
14     
15     // 方向的排序顺序
16     private Direction[] sortOrder = new[] {Direction.Up, Direction.Down, Direction.Left, Direction.Right};
17     
18     private Stack<Direction> directions = new Stack<Direction>();  // 当前的方向列表
19     
20     public PlayerMovement(int x, int y)
21     {
22         this.Position = new Tile(x, y);  // 初始化玩家的位置
23     }
24     
25     public void Update()
26     {
27         Console.WriteLine("Called update");  // 输出调试信息
28         
29         // 获取当前按下的方向键
30         var pressedDirections = Enum.GetValues(typeof(Direction)).Cast<Direction>().Where(x => Input.GetState(x));
31         
32         // 清除不再按下的方向键
33         while (directions.Count != 0 && !pressedDirections.Contains(directions.Peek())) {
34             directions.Pop();
35         }
36         
37         if (pressedDirections.Count() == 0) {
38             return;  // 如果没有按下任何方向键,则直接返回
39         }
40         
41         // 获取新按下的方向键,并按照排序顺序进行排序
42         var newDirections = pressedDirections.Except(directions).OrderBy(x => Array.IndexOf(sortOrder, x)).ToArray();
43         
44         // 将新的方向键加入到方向列表中
45         for (int i = newDirections.Length - 1; i >= 0; i -= 1) {
46             directions.Push(newDirections[i]);
47         }
48         
49         Direction directionToMove = directions.Peek();  // 获取要移动的方向
50         
51         if (directionToMove == this.Direction) {
52             // 如果要移动的方向与当前方向相同,则更新玩家的位置
53             (int xDelta, int yDelta) = moves[directionToMove];
54             this.Position = new Tile(this.Position.X + xDelta, this.Position.Y + yDelta);
55         }
56         else {
57             // 如果要移动的方向与当前方向不同,则更新玩家的方向
58             this.Direction = directionToMove;
59         }
60     }
61 }

算法2的运行步骤如下:

  1. 获取当前按下的方向键。
  2. 清除不再按下的方向键。
  3. 如果没有按下任何方向键,则直接返回。
  4. 获取新按下的方向键,并按照排序顺序进行排序。
  5. 将新的方向键加入到方向列表中。
  6. 获取要移动的方向。
  7. 如果要移动的方向与当前方向相同,则更新玩家的位置。
  8. 如果要移动的方向与当前方向不同,则更新玩家的方向。

优点:

  1. 实现简单,易于理解。
  2. 可以处理多个方向键同时按下的情况。

缺点:

  1. 在处理多个方向键同时按下的情况时,可能会出现按键顺序和预期不一致的情况。
  2. 对于复杂的移动逻辑,可能需要添加更多的代码来处理。

 

总结,这两个PlayerMovement类都是面向对象的,但是它们的实现方式略有不同:

1. 数据封装

算法2类使用属性来封装位置和方向,而算法1类也使用属性来封装位置和方向。两者的数据封装方式相同。

2. 代码组织

算法2类使用一个Update方法来更新玩家位置和方向,而算法1类也使用一个Update方法来更新玩家位置和方向。两者的代码组织方式相同。

3. 代码复用

算法2类使用了一个moves字典来存储方向和移动量的对应关系,而算法1类则没有使用字典,而是使用了GetCurrentDirections方法和GetNewPosition方法来计算新的方向和位置。两者的代码复用方式略有不同。

4. 可扩展性

算法2类使用了一个sortOrder数组来定义方向的排序,而算法1类则没有使用这种方式。算法1类使用了GetNewDirections方法,利用yield return特性来计算新的方向,这种方式更加灵活和可扩展。

它们都具有良好的数据封装和代码组织,以及可扩展性和代码复用性。其中,算法1类更加灵活和可扩展,适用于更加复杂的场景,例如:

  1. 虚拟现实和增强现实应用:在虚拟现实和增强现实应用中,用户通常需要通过手柄、控制器或手势来控制虚拟对象的移动。PlayerMovement算法可以用于处理用户输入的方向指令,并更新虚拟对象的位置和方向。

  2. 机器人控制:在机器人控制领域,PlayerMovement算法可以用于处理机器人的移动指令。例如,将方向键映射到机器人的移动方向,并根据按键的顺序来确定机器人的移动路径。

  3. 自动驾驶系统:在自动驾驶系统中,PlayerMovement算法可以用于处理车辆的移动指令。例如,将方向键映射到车辆的转向角度,并根据按键的顺序来确定车辆的转向路径。

  4. 智能家居系统:在智能家居系统中,PlayerMovement算法可以用于处理家居设备的移动指令。例如,将方向键映射到智能灯泡的亮度和颜色,并根据按键的顺序来调整灯泡的状态。


测试用例:

  1 using NUnit.Framework;
  2 using System;
  3 using System.Collections.Generic;
  4 using System.Linq;
  5 
  6 namespace TopDownMovement
  7 {
  8   [TestFixture]
  9   public class SolutionTest
 10   {
 11       private PlayerMovement _player;
 12   
 13       private void TestEquality(Direction direction, int x, int y)
 14       {
 15           _player.Update();
 16   
 17           Assert.AreEqual(direction, _player.Direction);
 18           Assert.AreEqual(new Tile(x, y), _player.Position);
 19       }
 20   
 21       [Test(Description = "Basic Test 1")]
 22       public void BasicTest1()
 23       {
 24           _player = new PlayerMovement(0, 0);
 25           Input.Clear();
 26 
 27           Press(Direction.Down);
 28 
 29           TestEquality(Direction.Down, 0, 0);
 30           TestEquality(Direction.Down, 0, -1);
 31           TestEquality(Direction.Down, 0, -2);
 32 
 33           Press(Direction.Left);
 34           Press(Direction.Right);
 35 
 36           TestEquality(Direction.Left, 0, -2);
 37           TestEquality(Direction.Left, -1, -2);
 38 
 39           Release(Direction.Left);
 40 
 41           TestEquality(Direction.Right, -1, -2);
 42 
 43           Release(Direction.Right);
 44 
 45           TestEquality(Direction.Down, -1, -2);
 46           TestEquality(Direction.Down, -1, -3);
 47 
 48           Release(Direction.Down);
 49 
 50           TestEquality(Direction.Down, -1, -3);
 51       }
 52 
 53       [Test(Description = "All keys at once")]
 54       public void BasicTest2()
 55       {
 56           _player = new PlayerMovement(0, 0);
 57           Input.Clear();
 58 
 59           Press(Direction.Down);
 60           Press(Direction.Left);
 61           Press(Direction.Right);
 62           Press(Direction.Up);
 63 
 64           TestEquality(Direction.Up, 0, 0);
 65           TestEquality(Direction.Up, 0, 1);
 66 
 67           Release(Direction.Left);
 68 
 69           TestEquality(Direction.Up, 0, 2);
 70 
 71           Release(Direction.Up);
 72 
 73           TestEquality(Direction.Down, 0, 2);
 74 
 75           Release(Direction.Down);
 76 
 77           TestEquality(Direction.Right, 0, 2);
 78           TestEquality(Direction.Right, 1, 2);
 79           TestEquality(Direction.Right, 2, 2);
 80 
 81           Release(Direction.Right);
 82 
 83           TestEquality(Direction.Right, 2, 2);
 84       }
 85 
 86       [Test(Description = "Random Test")]
 87       public void RandomTest()
 88       {
 89           int x, y;
 90           Random rand = new Random();
 91 
 92           x = rand.Next(200) - 100;
 93           y = rand.Next(200) - 100;
 94 
 95           _player = new PlayerMovement(x, y);
 96           Input.Clear();
 97 
 98           Press(Direction.Down);
 99           Press(Direction.Left);
100           Press(Direction.Right);
101           Press(Direction.Up);
102 
103           TestEquality(Direction.Up, x, y);
104 
105           for (int i = 0; i < rand.Next(20) + 1; i++)
106           {
107               y++;
108               TestEquality(Direction.Up, x, y);
109           }
110 
111           Release(Direction.Left);
112 
113           y++;
114           TestEquality(Direction.Up, x, y);
115 
116           Release(Direction.Up);
117 
118           TestEquality(Direction.Down, x, y);
119 
120           Release(Direction.Down);
121 
122           TestEquality(Direction.Right, x, y);
123           x++;
124           TestEquality(Direction.Right, x, y);
125           x++;
126           TestEquality(Direction.Right, x, y);
127 
128           Release(Direction.Right);
129 
130           TestEquality(Direction.Right, x, y);
131       }
132 
133       private void Press(Direction dir) { Console.WriteLine("Pressed " + dir); Input.Press(dir); }
134       private void Release(Direction dir) { Console.WriteLine("Released " + dir); Input.Release(dir); }
135   }
136 }

 

与【算法】游戏中的学习,使用c#面向对象特性控制游戏角色移动相似的内容:

【算法】游戏中的学习,使用c#面向对象特性控制游戏角色移动

最近,小悦的生活像是一首繁忙的交响曲,每天忙得团团转,虽然她的日程安排得满满当当,但她并未感到充实。相反,她很少有时间陪伴家人,这让她感到有些遗憾。在周五的午后,小悦的哥哥突然打来电话,他的声音里充满了焦虑。 “小悦,我有个事情想拜托你。”哥哥的声音传来。 小悦不禁有些疑惑,哥哥有什么事情需要她帮忙

动手实践丨基于ModelAtrs使用A2C算法制作登月器着陆小游戏

摘要:在本案例中,我们将展示如何基于A2C算法,训练一个LunarLander小游戏。 本文分享自华为云社区《使用A2C算法控制登月器着陆》,作者:HWCloudAI 。 LunarLander是一款控制类的小游戏,也是强化学习中常用的例子。游戏任务为控制登月器着陆,玩家通过操作登月器的主引擎和副引

Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形

原文: Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形-Stars-One的杂货小窝 在B站刷到了随机地图生成的视频,随手学习下并做下记录 注: 本篇使用javafx应用作演示,算是了解这个算法的使用,后续会再出篇libgdx生成地图的示例 说明 抛开算法实现,首先认知柏林噪音算法 一般

策略梯度玩 cartpole 游戏,强化学习代替PID算法控制平衡杆

cartpole游戏,车上顶着一个自由摆动的杆子,实现杆子的平衡,杆子每次倒向一端车就开始移动让杆子保持动态直立的状态,策略函数使用一个两层的简单神经网络,输入状态有4个,车位置,车速度,杆角度,杆速度,输出action为左移动或右移动,输入状态发现至少要给3个才能稳定一会儿,给2个完全学不明白,给

想知道海外技术面试都考些什么吗?

本文主要分享了一位求职者在游戏国际海外数据与发行技术团队面试中的经历,包括一面和二面的详细问题。一面中涉及了算法题、SQL题、项目相关问题以及技术细节的深入探讨,如乐观锁、RabbitMQ、Redis的应用等。二面则更侧重于对项目实施过程的反思、技术方案的选择以及实习时间的确认

如何让虚拟角色自然融入现实?

随着AR的发展,虚拟角色被广泛应用在游戏、直播、社交等App中。例如在直播App里,商家可以自由打造虚拟主播的形象,通过AR算法可以让虚拟形象在介绍时做到不遮挡实物商品,提升直播真实性和趣味性。那么,如何让虚拟角色自然融入现实,实现与用户的真实交互呢? 华为HMS Core AR Engine提供单

KPM算法求字符串的最小周期证明

先给出公式 ans = n - LPS[n-1] 其中ans为最小周期,n为给出的由假设的周期字符串中提取出的子串长度,LPS为前缀函数,n-1为字符串最后的位置下标 证明如下 证明ans = n - LPS[n-1],思路: (1) 证明特殊情况,即先对完整周期字符串进行证明,这时候的字符串组成是

在代码世界游走,没几把“锁”防身可不行

锁共有多种算法,在并发场景中都是被常常用到,想必大家都已炉火纯青般.....巴特!我们还有后浪同学们可能不熟悉,那我在这里聊下锁的用法和使用场景。

[转帖]关于一致性哈希算法在游戏服务器端的思考

https://www.jianshu.com/p/b8ae27cf22a9 突然想明白 其实网易的将军令 就是一个一致性哈希的玩法 关于一致性哈希算法在游戏服务器端的思考 需求分析 后端有很多逻辑node节点(not-section binded),节点启动后注册到注册中心 node本身有状态,有

【算法】用c#实现德州扑克卡牌游戏规则

德州扑克是一种牌类游戏,可多人参与,它的玩法是,玩家每人发两张底牌,桌面依次发5张公共牌,玩家用自己的两张底牌和5张公共牌自由组合,按大小决定胜负。 使用c#完成功能Hand()以返回手牌类型和按重要性递减顺序排列的等级列表,用于与同类型的其他手牌进行比较,即最佳手牌。 可能的手牌按价值降序排列: