题目描述
石头剪刀布游戏有 3 种出拳形状:石头、剪刀、布。分别用字母 A , B , C 表示。
游戏规则:
出拳形状之间的胜负规则如下: A > B;B > C;C > A;">"左边一个字母,表示相对优势形状。右边一个字母,表示相对劣势形状。
当本场次中有且仅有一种出拳形状优于其它出拳形状,则该形状的玩家是胜利者。否则认为是平局。
当发生平局,没有赢家。有多个胜利者时,同为赢家。
- 例如 1: 三个玩家出拳分别是A, B, C ,由于出现三方优势循环(即没有任何一方优于其它出拳者),判断为平局。
- 例如 2: 两个玩家,出拳分别是 A, B ,出拳 A 的获胜。
- 例如 3: 三个玩家,出拳全部是 A ,判为平局。
输入描述
在一场游戏中,每个玩家的信息为一行。玩家数量不超过 1000
。每个玩家信息有 2
个字段,用空格隔开:
-
玩家 ID:一个仅由英文字母和数字组成的字符串
-
出拳形状:以英文大写字母表示,
A 、B 、C
形状。 例:abc1 A
xyz B
12
输出描述
输出为赢家的玩家 ID 列表(一个或多个),每个 ID 一行,按字符串升序排列。如果没有赢家,输出为"NULL"
字符串。例如:
abc1
用例1
输入
abc1 A
xyz B
输出
abc1
说明
A比B有优势,abc1 胜出
用例2
输入
abc1 A
xyz A
输出
NULL
说明
没有优胜的出拳形状,平局
用例3
输入
abc1 A
def A
alic A
xyz B
输出
abc1
alic
def
说明
A为优胜方,有三个赢家
思路
解题思路如下:
-
读取输入:通过
scanf
函数逐行读取每个玩家的 ID 和出拳形状,存储在 Player 结构体数组中,并记录有效玩家数量(即 count 变量)。 -
统计各出拳形状的数量:遍历 Player 结构体数组,分别计算出拳为 ‘A’、‘B’ 和 ‘C’ 的玩家数量(countA、countB、countC)。
-
判断游戏结果:
- 检查是否存在三种出拳形状都出现的情况,且没有任何一种形状的数量大于另一种形状,这种情况表示平局,输出 “NULL”。
- 检查是否存在某一种形状的所有玩家都选择同一种出拳,这也是一种平局情况,同样输出 “NULL”。
-
确定赢家:
- 根据前面统计得到的 countA、countB、countC,检查是否有某种形状不存在(即数量为 0),若有,从另外两个形状中选择赢家,并输出对应出拳形状的赢家玩家ID。
通过以上步骤,程序即可根据石头剪刀布的游戏规则正确解析输入信息并输出赢家列表。
代码
// 石头剪刀布游戏代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义玩家结构体,包含玩家ID(name)和出拳形状(play)
typedef struct {
char name[10]; // 玩家ID,由英文字母和数字组成,最大长度为9个字符
char play; // 玩家出拳形状,用英文大写字母'A'、'B'或'C'表示石头、剪刀、布
} Player;
int main() {
// 初始化一个可存储100个玩家信息的数组
Player player[100];
int count = 0; // 记录有效输入玩家的数量
// 读取玩家输入,直到文件结束符EOF
while (scanf("%s %c", player[count].name, &player[count].play) != EOF) {
count++; // 每成功读取一组玩家信息,计数器加1
}
// 统计各出拳形状的数量
int countA = 0, countB = 0, countC = 0;
for (int i = 0; i < count; i++) {
if (player[i].play == 'A') {
countA++;
}
if (player[i].play == 'B') {
countB++;
}
if (player[i].play == 'C') {
countC++;
}
}
// 判断游戏结果:
// 1. 如果三种出拳形状都出现,并且没有一种形状数量大于另一种形状,则平局
// 2. 如果某一种形状的数量等于总玩家数,则也是平局
if ((countA > 0 && countB > 0 && countC > 0) ||
(countA == count || countB == count || countC == count)) {
printf("NULL\n"); // 输出“NULL”,表示没有赢家
return 0;
}
// 根据剩余形状确定赢家
if (countA == 0) { // 剩余B和C,说明B是赢家
for (int i = 0; i < count; i++) {
if (player[i].play == 'B') {
printf("%s\n", player[i].name); // 输出赢家ID
}
}
}
if (countB == 0) { // 剩余A和C,说明C是赢家
for (int i = 0; i < count; i++) {
if (player[i].play == 'C') {
printf("%s\n", player[i].name); // 输出赢家ID
}
}
}
if (countC == 0) { // 剩余A和B,说明A是赢家
for (int i = 0; i < count; i++) {
if (player[i].play == 'A') {
printf("%s\n", player[i].name); // 输出赢家ID
}
}
}
return 0;
}
注意
1、scanf的返回值
scanf()
是 C 语言中用于从标准输入读取数据的标准库函数,它根据指定的格式字符串从 stdin(通常为键盘)读取并解析输入的数据。scanf()
函数返回一个整数值,该值表示成功读取和转换的参数个数。
以下是 scanf()
返回值的详细说明:
-
成功读取与转换:
- 当
scanf()
成功读取并转换了与格式化字符串中相应格式说明符匹配的数据时,它会返回读取成功的变量或字段的数量。 - 例如,如果你有
scanf("%d %f", &num, &flt);
并且用户正确地输入了一个整数和一个浮点数,scanf()
将返回2,因为两个变量都成功读取。
- 当
-
部分成功:
- 如果格式化字符串中有多个占位符,但只有一部分被成功读取,则返回值是成功读取的参数个数。
- 如
scanf("%d%d", &a, &b);
用户仅输入了一个整数,那么scanf()
将返回1,表示成功读取了一个整数,第二个变量没有被赋值。
-
失败与错误:
- 当
scanf()
遇到无法转换的数据、到达文件末尾(EOF)、或者发生其他错误时,它将返回0。 - 若遇到非法输入或预期类型与实际输入不符的情况,后续的输入项将不会被读取,并且整个函数调用将立即终止。
- 在读取过程中如果碰到 EOF (End Of File),在Unix/Linux系统下通常是通过按下组合键Ctrl+D,在Windows系统下通常是按下Ctrl+Z然后回车来模拟的,此时
scanf()
的返回值可能为EOF(通常定义为-1,具体取决于编译器实现)。
- 当
-
缓冲区问题与安全:
- 注意,由于
scanf()
不清除输入流中的未处理字符,所以连续调用scanf()
可能导致意外结果,尤其是在处理用户输入时应特别小心,考虑使用fgets()
结合sscanf()
或其他更安全的方式来读取和解析输入。
- 注意,由于
总结来说,scanf()
的返回值是一个非常重要的信息,它可以用来判断读取操作是否按预期完成,以及有多少个变量成功接收到了有效的输入数据。开发人员应当始终检查 scanf()
的返回值以确保程序正确处理所有可能的输入情况。