小时候一个人无聊的时候最喜欢玩的游戏,一副扑克牌每次发4张,加减乘除算得24,玩的贼溜。曾今在初中的时候有次和同学玩算24点,几乎次次在牌亮出后我就能说出公式,惊呆了我的小伙伴~~~
最近又想起了这个小游戏,于是利用工作之余写了一个C#版的算24点,怀恋下小时候的情怀!
啥都不说了,直接上代码。
//Program.cs
class Program
{
static void Main(string[] args)
{
Console.WriteLine(" ***************************************************");
Console.WriteLine(" * 扑克牌算24 *");
Console.WriteLine(" * Copyright 2021 凌风游 *");
Console.WriteLine(" * https://gitee.com/lkj1420428992/poker24 *");
Console.WriteLine(" ***************************************************");
Console.WriteLine();
Console.WriteLine(" 用+-*/运算,算出结果24。AJQK作为数字1");
Console.WriteLine(" play:开始发牌; exit:退出; answer:查看答案");
bool isExit = false;
PlayPoker playPoker = new PlayPoker();
while (!isExit) {
string msg = Console.ReadLine();
if (msg == "exit") {
isExit = true;
break;
}
else if (msg == "play") {
string title = playPoker.Play();
Console.WriteLine($"发牌:{title} 请写出公式:");
Console.WriteLine();
string formula = playPoker.InputFormula();
if (formula == "answer")
{
Console.WriteLine("正确答案:");
showAnswer(playPoker.PokerResult());
}
else {
var res = playPoker.CheckFormula(formula);
if (res)
{
Console.WriteLine("回答正确");
}
else {
Console.WriteLine("回答错误");
Console.WriteLine("正确答案:");
showAnswer(playPoker.PokerResult());
}
}
}
}
}
static void showAnswer(List<string> answers) {
if (answers == null || answers.Count == 0) return;
answers.ForEach(item=> {
Console.WriteLine(item);
});
}
}
//PlayPoker.cs
public class PlayPoker
{
public string[] Pokers = new string[13] {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
private string pokerA, pokerB, pokerC, pokerD = null;
private DataTable dataTable = new DataTable();
private Poker24 p24 = new Poker24();
//发牌
public string Play() {
pokerA = GetRandomPoker();
pokerB = GetRandomPoker();
pokerC = GetRandomPoker();
pokerD = GetRandomPoker();
return $"{pokerA} {pokerB} {pokerC} {pokerD}";
}
private string ConvertPoker(string poker) {
string res = null;
switch (poker) {
case "A":
res = "1";
break;
case "J":
res = "1";
break;
case "Q":
res = "1";
break;
case "K":
res = "1";
break;
default:
res = poker;
break;
}
return res;
}
//随机获取扑克牌
private string GetRandomPoker() {
byte[] buffer = Guid.NewGuid().ToByteArray();
int iSeed = BitConverter.ToInt32(buffer, 0);
Random ran = new Random(iSeed);
int index = ran.Next(0, Pokers.Length);
return Pokers[index];
}
//输入公式
public string InputFormula() {
string formula = string.Empty;
Boolean isComplete = false;
while (!isComplete) {
formula = Console.ReadLine();
isComplete = true;
}
return formula;
}
//检查公式结果是否正确
public Boolean CheckFormula(string formula) {
try {
var res = dataTable.Compute(formula, null).ToString();
return res == "24";
}
catch (Exception err) {
return false;
}
}
//返回所有结果
public List<string> PokerResult() {
try {
p24.DealFourCards(ConvertPoker(pokerA), ConvertPoker(pokerB), ConvertPoker(pokerC), ConvertPoker(pokerD));
var res = p24.Calc24();
if (res == null || res.Count == 0) return null;
return res.Select(x => $"{x.Formula()} = 24").ToList();
}
catch (Exception err) {
return null;
}
}
}
//Poker24.cs
public sealed class Poker24
{
private string[] poker = new string[4];
private string[] signArr = new string[4] { "+", "-", "*", "/" };
public Poker24()
{
}
public void DealFourCards(string pokerA, string pokerB, string pokerC, string pokerD)
{
poker[0] = pokerA;
poker[1] = pokerB;
poker[2] = pokerC;
poker[3] = pokerD;
}
public List<CalcBlock> Calc24()
{
List<CalcBlock> pbs = new List<CalcBlock>();
List<string[]> list = new List<string[]>();
//4个数字共有4*3*2*1=24种排列组合
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (i == j) continue;
for (int k = 0; k < 4; k++)
{
if (i == k || j == k) continue;
for (int m = 0; m < 4; m++)
{
if (i == m || j == m || k == m) continue;
list.Add(new string[4] { poker[i], poker[j], poker[k], poker[m] });
}
}
}
}
//三个=-*/位置共有4*4*4=64种组合
List<string[]> signList = new List<string[]>();
for (int i = 0; i < signArr.Length; i++)
{
for (int j = 0; j < signArr.Length; j++)
{
for (int k = 0; k < signArr.Length; k++)
{
signList.Add(new string[3] { signArr[i], signArr[j], signArr[k] });
}
}
}
//数字和符号位 组合共有 24*64=1536种组合
List<Tuple<string[], string[]>> formulaList = new List<Tuple<string[], string[]>>();
for (int i = 0; i < list.Count; i++)
{
for (int j = 0; j < signList.Count; j++)
{
formulaList.Add(new Tuple<string[], string[]>(list[i], signList[j]));
}
}
//共有6中括号位置可能 共有1536*6=9216种组合
foreach (var item in formulaList)
{
string[] a = item.Item1;
string[] b = item.Item2;
//筛选结果等于24的计算公式
var af1 = AnalyticFormula($"{a[0]}{b[0]}{a[1]}{b[1]}{a[2]}{b[2]}{a[3]}");
if (af1.Calc() == 24) pbs.Add(af1);
af1 = AnalyticFormula($"({a[0]}{b[0]}{a[1]}){b[1]}{a[2]}{b[2]}{a[3]}");
if (af1.Calc() == 24) pbs.Add(af1);
af1 = AnalyticFormula($"({a[0]}{b[0]}{a[1]}{b[1]}{a[2]}){b[2]}{a[3]}");
if (af1.Calc() == 24) pbs.Add(af1);
af1 = AnalyticFormula($"{a[0]}{b[0]}({a[1]}{b[1]}{a[2]}){b[2]}{a[3]}");
if (af1.Calc() == 24) pbs.Add(af1);
af1 = AnalyticFormula($"{a[0]}{b[0]}({a[1]}{b[1]}{a[2]}{b[2]}{a[3]})");
if (af1.Calc() == 24) pbs.Add(af1);
af1 = AnalyticFormula($"{a[0]}{b[0]}{a[1]}{b[1]}({a[2]}{b[2]}{a[3]})");
if (af1.Calc() == 24) pbs.Add(af1);
}
//排除重复的计算公式
pbs = pbs.Distinct(new CalcPokerBlockComparer()).ToList();
//交换律排除重复的计算公式
ExcludeOfExchange(pbs);
return pbs;
}
/// <summary>
/// 分析计算公式,将字符串计算公式解析为CalcBlock
/// </summary>
private CalcBlock AnalyticFormula(string formula)
{
var b = formula.ToArray();
Stack<PokerBlock> stacks = new Stack<PokerBlock>();
List<PokerBlock> list = new List<PokerBlock>();
string c = "";
for (var i = 0; i < b.Length; i++)
{
if (char.IsDigit(b[i]))
{
c += b[i];
if (i == b.Length - 1) list.Add(new ValueBlock(double.Parse(c)));
}
else
{
if (c != "") list.Add(new ValueBlock(double.Parse(c)));
list.Add(new SignBlock(b[i].ToString()));
c = "";
}
}
//利用栈逐步解析公式
int signCount = 0;
SignBlock preSign = null;
foreach (var item in list)
{
var sb = item as SignBlock;
if (sb != null) //符号
{
if (sb.IsRightBracket)
{
if (preSign != null && signCount > 1)
{
SignPop(stacks);
}
RightBracketPop(stacks);
preSign = null;
signCount = 0;
}
else if (sb.IsLeftBracket)
{
preSign = null;
signCount = 0;
stacks.Push(item);
}
else
{
if (preSign == null)
{
preSign = sb;
signCount++;
stacks.Push(item);
}
else
{
if (sb.ComparePriority(preSign) <= 0)
{
preSign = sb;
signCount = 1;
SignPop(stacks);
stacks.Push(item);
}
else
{
preSign = sb;
signCount++;
stacks.Push(item);
}
}
}
}
else
{ //数字
stacks.Push(item);
}
}
End(stacks);
var res = stacks.Pop();
return res as CalcBlock;
}
/// <summary>
/// 右括号出栈
/// </summary>
private void RightBracketPop(Stack<PokerBlock> stacks)
{
var d = stacks.Pop(); // 3
var c = stacks.Pop(); // +
var b = stacks.Pop(); // 5
var a = stacks.Pop(); // (
var e = c as SignBlock;
var f = e.Merge(b as CalcBlock, d as CalcBlock);
stacks.Push(f);
}
// +-*/符号出栈
private void SignPop(Stack<PokerBlock> stacks)
{
var c = stacks.Pop(); // 3
var b = stacks.Pop(); // +
var a = stacks.Pop(); // 5
var d = b as SignBlock;
var e = d.Merge(a as CalcBlock, c as CalcBlock);
stacks.Push(e);
}
/// <summary>
/// 最后全部出栈
/// </summary>
private void End(Stack<PokerBlock> stacks)
{
if (stacks.Count == 1) return;
var c = stacks.Pop(); // 3
var b = stacks.Pop(); // +
var a = stacks.Pop(); // 5
var d = b as SignBlock;
var e = d.Merge(a as CalcBlock, c as CalcBlock);
stacks.Push(e);
End(stacks);
}
/// <summary>
/// 交换律排除重复
/// </summary>
private void ExcludeOfExchange(List<CalcBlock> pbs)
{
List<CalcBlock> removePbs = new List<CalcBlock>();
for (int i = 0; i < pbs.Count - 1; i++)
{
string exchangeFormula = pbs[i].LawOfExchange();
if (exchangeFormula == null) continue;
for (int j = i + 1; j < pbs.Count; j++)
{
string formula = pbs[j].Formula();
if (exchangeFormula == formula)
{
removePbs.Add(pbs[j]);
}
}
}
if (removePbs.Count > 0) removePbs.ForEach(item => { pbs.Remove(item); });
}
}
//PokerBlock.cs
/// <summary>
/// 基础块
/// </summary>
public abstract class PokerBlock
{
}
/// <summary>
/// 计算块
/// </summary>
public abstract class CalcBlock : PokerBlock
{
public abstract string Formula();
public abstract double Calc();
/// <summary>
/// 交换律
/// </summary>
public virtual string LawOfExchange()
{
return null;
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (this.GetType() != obj.GetType()) return false;
return Equals((CalcBlock)obj);
}
public bool Equals(CalcBlock obj)
{
if (obj == null) return false;
if (ReferenceEquals(this, obj)) return true;
return (Formula() == obj.Formula());
}
public static bool operator ==(CalcBlock p1, CalcBlock p2)
{
if (ReferenceEquals(p1, null)) return ReferenceEquals(p2, null);
return (p1.Equals(p2));
}
public static bool operator !=(CalcBlock p1, CalcBlock p2)
{
return !(p1 == p2);
}
public override int GetHashCode()
{
string a = Formula();
return a.GetHashCode();
}
}
/// <summary>
/// 符号块
/// </summary>
public sealed class SignBlock : PokerBlock
{
private string Sign { get; set; }
public SignBlock(string sign)
{
this.Sign = sign;
}
/// <summary>
/// 比较优先级 大于0 优先级高,等于0相等,小于0 优先级小
/// </summary>
public int ComparePriority(SignBlock sb)
{
return this.GetPriority() - sb.GetPriority();
}
public Boolean IsLeftBracket
{
get
{
return Sign == "(";
}
}
public Boolean IsRightBracket
{
get
{
return Sign == ")";
}
}
public int GetPriority()
{
int p = 0;
switch (Sign)
{
case "+":
p = 10;
break;
case "-":
p = 10;
break;
case "*":
p = 20;
break;
case "/":
p = 20;
break;
}
return p;
}
public CalcBlock Merge(CalcBlock a, CalcBlock b)
{
CalcBlock res = null;
switch (Sign)
{
case "+":
res = new AddCalcBlock(a, b);
break;
case "-":
res = new SubCalcBlock(a, b);
break;
case "*":
res = new MultiCalcBlock(a, b);
break;
case "/":
res = new DivisionCalcBlock(a, b);
break;
}
return res;
}
}
/// <summary>
/// 数值块
/// </summary>
public sealed class ValueBlock : CalcBlock
{
private double Value { get; set; }
public ValueBlock(double value)
{
this.Value = value;
}
public override double Calc()
{
return Value;
}
public override string Formula()
{
return Value.ToString();
}
}
/// <summary>
/// 加法块
/// </summary>
public sealed class AddCalcBlock : CalcBlock
{
private CalcBlock LeftBlock { get; set; }
private CalcBlock RightBlock { get; set; }
public AddCalcBlock(CalcBlock leftBlock, CalcBlock rightBlock)
{
this.LeftBlock = leftBlock;
this.RightBlock = rightBlock;
}
public override double Calc()
{
return LeftBlock.Calc() + RightBlock.Calc();
}
public override string Formula()
{
return $"{LeftBlock.Formula()} + {RightBlock.Formula()}";
}
public override string LawOfExchange()
{
return $"{RightBlock.Formula()} + {LeftBlock.Formula()}";
}
}
/// <summary>
/// 减法块
/// </summary>
public sealed class SubCalcBlock : CalcBlock
{
private CalcBlock LeftBlock { get; set; }
private CalcBlock RightBlock { get; set; }
public SubCalcBlock(CalcBlock leftBlock, CalcBlock rightBlock)
{
this.LeftBlock = leftBlock;
this.RightBlock = rightBlock;
}
public override double Calc()
{
return LeftBlock.Calc() - RightBlock.Calc();
}
public override string Formula()
{
if (RightBlock is SubCalcBlock)
{
return $"{LeftBlock.Formula()} - ( {RightBlock.Formula()} )";
}
return $"{LeftBlock.Formula()} - {RightBlock.Formula()}";
}
}
/// <summary>
/// 乘法块
/// </summary>
public sealed class MultiCalcBlock : CalcBlock
{
private CalcBlock LeftBlock { get; set; }
private CalcBlock RightBlock { get; set; }
public MultiCalcBlock(CalcBlock leftBlock, CalcBlock rightBlock)
{
this.LeftBlock = leftBlock;
this.RightBlock = rightBlock;
}
public override double Calc()
{
return LeftBlock.Calc() * RightBlock.Calc();
}
public override string Formula()
{
string left = "";
string right = "";
if (LeftBlock is AddCalcBlock || LeftBlock is SubCalcBlock)
{
left = $"( {LeftBlock.Formula()} )";
}
else
{
left = LeftBlock.Formula();
}
if (RightBlock is AddCalcBlock || RightBlock is SubCalcBlock)
{
right = $"( {RightBlock.Formula()} )";
}
else
{
right = RightBlock.Formula();
}
return $"{left} * {right}";
}
public override string LawOfExchange()
{
string left = "";
string right = "";
if (LeftBlock is AddCalcBlock || LeftBlock is SubCalcBlock)
{
left = $"( {LeftBlock.Formula()} )";
}
else
{
left = LeftBlock.Formula();
}
if (RightBlock is AddCalcBlock || RightBlock is SubCalcBlock)
{
right = $"( {RightBlock.Formula()} )";
}
else
{
right = RightBlock.Formula();
}
return $"{right} * {left}";
}
}
/// <summary>
/// 除法块
/// </summary>
public sealed class DivisionCalcBlock : CalcBlock
{
private CalcBlock LeftBlock { get; set; }
private CalcBlock RightBlock { get; set; }
public DivisionCalcBlock(CalcBlock leftBlock, CalcBlock rightBlock)
{
this.LeftBlock = leftBlock;
this.RightBlock = rightBlock;
}
public override double Calc()
{
return LeftBlock.Calc() / RightBlock.Calc();
}
public override string Formula()
{
string left = "";
string right = "";
if (LeftBlock is AddCalcBlock || LeftBlock is SubCalcBlock)
{
left = $"( {LeftBlock.Formula()} )";
}
else
{
left = LeftBlock.Formula();
}
if (RightBlock is AddCalcBlock || RightBlock is SubCalcBlock)
{
right = $"( {RightBlock.Formula()} )";
}
else
{
right = RightBlock.Formula();
}
return $"{left} / {right}";
}
}
public sealed class CalcPokerBlockComparer : IEqualityComparer<CalcBlock>
{
public bool Equals(CalcBlock x, CalcBlock y)
{
if (x == null)
return y == null;
return x == y;
}
public int GetHashCode(CalcBlock obj)
{
if (obj == null)
return 0;
return obj.GetHashCode();
}
}
MySQL常见问题及答案汇总,MySQL是一种开放源代码的关系型数据库管理系统。数据...
最近手里有个项目需要用iframe来调用每天都会变化的页面,后来想到iframe会不会...
用ajax实现对数据库的查询以及对查询数据进行分页,供大家参考,具体内容如下 主...
经过摸索和实践,我把自己的解决方法,写在下面: 说明: 我的Oracle客户端的版...
图片来自 Pexels 说实话,这是一次比较曲折的 Bug 跟踪之旅。10 月 28 日,我们...
复制代码 代码如下: % str = request("str") reg = request("reg") set regex = ...
之前一直在写JQUERY代码的时候遇到AJAX加载数据都需要考虑代码运行顺序问题。最...
smart.asp 复制代码 代码如下: script language="jscript" runat="server" /* 在...
功能:$1-$9存放着正则表达式中最近的9个正则表达式的匹配结果,这些结果按照子...
最近在学习STM8这块单片机但是在网上STM8的资料实在是太少了而且很多东西都比较...