C#使用代碼實(shí)現(xiàn)春晚撲克牌魔術(shù)
前言
2024年春晚上,表演了一個(gè)魔術(shù)《守歲共此時(shí)》,博主也跟著做了一遍,確實(shí)都成功了。
對(duì)于此魔術(shù)的好奇心,博主從代碼邏輯的角度思考了下,肯定是可以實(shí)現(xiàn)的,畢竟代碼只是一個(gè)工具,只要實(shí)際邏輯合理基本沒問題。
本篇文章將通過代碼步驟進(jìn)行描述和實(shí)現(xiàn)。
實(shí)際上,了解原理后,使用遞歸方法和循環(huán)隊(duì)列去實(shí)現(xiàn)是比較合理的。
實(shí)現(xiàn)步驟
為了方便查看,每一個(gè)步驟都把變化前的撲克牌保存到一個(gè)泛型變量里
設(shè)置變量
姓名、來自區(qū)域、以及性別,這三者為變量,決定每個(gè)人手上牌的順序會(huì)不一樣。
string myName = "小美"; string positionValue = "北方人"; string sex = "女生";
設(shè)置牌
設(shè)置好52張撲克牌
List<string> tempList = new List<string>(); string pokerStr = @"黑桃-A、黑桃-2、黑桃-3、黑桃-4、黑桃-5、黑桃-6、黑桃-7、黑桃-8、黑桃-9、黑桃-10、黑桃-J、黑桃-Q、黑桃-K、 紅桃-A、紅桃-2、紅桃-3、紅桃-4、紅桃-5、紅桃-6、紅桃-7、紅桃-8、紅桃-9、紅桃-10、紅桃-J、紅桃-Q、紅桃-K、 梅花-A、梅花-2、梅花-3、梅花-4、梅花-5、梅花-6、梅花-7、梅花-8、梅花-9、梅花-10、梅花-J、梅花-Q、梅花-K、 方塊-A、方塊-2、方塊-3、方塊-4、方塊-5、方塊-6、方塊-7、方塊-8、方塊-9、方塊-10、方塊-J、方塊-Q、方塊-K"; List<string> pokerList = pokerStr.Split('、').Select(a=>a.Replace("\r\n","").Trim()).ToList();
隨機(jī)牌
通過上一步驟設(shè)置的52張牌,隨機(jī)產(chǎn)生4張牌
// 隨機(jī)四張 List<string> myPokerList = new List<string>(); for(int i = 0; i < 4; i++) { int randomIndex = new Random().Next(0, pokerList.Count); string pokder = pokerList[randomIndex]; myPokerList.Add(pokder); pokerList.RemoveAt(randomIndex); }
撕開兩半
撕開兩半,并且右邊的一半放到底部,對(duì)于數(shù)組而言實(shí)際上就是放到首位
// 撕開2半 List<string> newList = new List<string>(); tempList = NewOrder(myPokerList); // 右半邊 for (int i = 0; i < tempList.Count; i++) { newList.Add($"{tempList[i]}-右"); } // 左半邊 for (int i = 0; i < tempList.Count; i++) { newList.Add($"{tempList[i]}-左"); }
姓名牌
每個(gè)人姓名長(zhǎng)度不一樣,順序也就不一樣,下面代碼實(shí)現(xiàn)比較邏輯,其實(shí)目標(biāo)就是把最上面的一張牌放到底部,多少個(gè)字就放幾次。
博主姓名是三個(gè)字,就依次放三張牌到底部。代碼實(shí)現(xiàn)邏輯就是數(shù)組最后一個(gè)值放到首位。
// 姓名數(shù)字 - 動(dòng)態(tài)值 int myNameLength = myName.Length; string nameString = string.Empty; List<string> nameList = new List<string>(); List<string> nameListMy = new List<string>(); for (int i = 0; i < myNameLength; i++) { string pv = newList[newList.Count - 1 - i]; nameListMy.Add(pv); nameString = $"{pv},{nameString}"; } for (int i = 0; i < newList.Count - myNameLength; i++) { nameString += $",{newList[i]}"; } nameList = nameString.Split(',').Where(a => !string.IsNullOrWhiteSpace(a)).ToList();
任意牌
拿起最上面3張牌,插入到任意牌的中間,1、2、3、4、5,1到5之間插入
// int topValue = 3; int randomValue = new Random().Next(1, nameList.Count - topValue - 1); List<string> top3List = new List<string>(); for (int i = 0; i < nameList.Count - topValue; i++) { if (i == randomValue) { for (int j = topValue; j > 0; j--) { top3List.Add(nameList[nameList.Count - j]); } } top3List.Add(nameList[i]); }
標(biāo)記牌
拿起最上面一張牌,就是黑桃3-右,放到一遍,在數(shù)組里就是移除此張牌,并保存到一個(gè)變量里
// 【第一次移除牌】 // 拿起最上面一張牌,就是黑桃3-右,放到一遍 string topPoker = top3List[top3List.Count - 1]; List<string> sevenList = new List<string>(); for (int i = 0; i < top3List.Count - 1; i++) { sevenList.Add(top3List[i]); }
區(qū)域牌
南方人:拿起一張牌、北方人:拿起兩張牌、不知道是南方還是北方人:拿起三張牌
插入到剩下牌的中間,1到5或者1到6之間插入
int fromValue = (positionValue == "南方人" ? 1 : positionValue == "北方人" ? 2 : 3); int randomValue2 = new Random().Next(1, sevenList.Count - fromValue - 1); List<string> fromList = new List<string>(); for (int i = 0; i < sevenList.Count - fromValue; i++) { if (i == randomValue2) { for (int j = fromValue; j > 0; j--) { fromList.Add(sevenList[sevenList.Count - j]); } } fromList.Add(sevenList[i]); }
性別牌
男生:拿起一張、女生:拿起兩張、拿起的牌,撒到空中去,就是從數(shù)組中移除
// 【再次移除牌】 int sexType = (sex == "男生" ? 1 : 2); List<string> sexList = new List<string>(); for (int i = 0; i < fromList.Count - sexType; i++) { sexList.Add(fromList[i]); }
循環(huán)牌
見證奇跡的時(shí)刻,這是七個(gè)字,就是循環(huán)遍歷7次,對(duì)于數(shù)組而言,就是循環(huán)從最后一個(gè)值移動(dòng)到第一個(gè)值。
string textValue = "見證奇跡的時(shí)刻"; List<string> forList = new List<string>(); for (int i = 0; i < sexList.Count; i++) { forList.Add(sexList[i]); } for (int i = 0; i < textValue.Length; i++) { string lastElement = forList[forList.Count - 1]; // 保存最后一個(gè)元素的值 forList.RemoveAt(forList.Count - 1); // 移除最后一個(gè)元素 forList.Insert(0, lastElement); // 將最后一個(gè)元素插入到第一個(gè)位置 }
好運(yùn)牌
最后一個(gè)環(huán)節(jié):好運(yùn)留下來,煩惱丟出去
第一張放到下面,然后最上面的丟出去,就是移除數(shù)組最后一個(gè)
List<string> lastList = new List<string>(); for (int i = 0; i < forList.Count; i++) { lastList.Add(forList[i]); } while (lastList.Count > 1) { string lastElement = lastList[lastList.Count - 1]; // 保存最后一個(gè)元素的值 lastList.RemoveAt(lastList.Count - 1); // 移除最后一個(gè)元素 lastList.Insert(0, lastElement); // 將最后一個(gè)元素插入到第一個(gè)位置 lastList.RemoveAt(lastList.Count - 1); } string lastPoker = lastList[0]; // 合并撲克牌 string allPoker = $"【{lastPoker}】【{topPoker}】";
約瑟夫原理
上面撲克牌魔術(shù)沒想到用到了這個(gè)原理,不是每個(gè)人都能快速理解各種各樣的原理,所以這也早就了魔術(shù)神奇而又神秘的一面。
約瑟夫原理(也稱為約瑟夫問題或約瑟夫環(huán))是一個(gè)著名的理論問題,其起源可以追溯到公元1世紀(jì)的一個(gè)歷史事件。據(jù)傳,著名猶太歷史學(xué)家Josephus和他的同伴被敵人包圍,在面臨絕境時(shí),他們決定通過自殺的方式結(jié)束生命。為了執(zhí)行這個(gè)決定,他們圍成一個(gè)圈,然后按照一定的規(guī)則來選擇自殺的人,直到只剩下最后一個(gè)人。Josephus作為一個(gè)不愿意自殺的人,快速地計(jì)算出了一個(gè)位置,使得他成為了最后一個(gè)存活的人,從而有機(jī)會(huì)逃脫。
這個(gè)問題可以用數(shù)學(xué)模型進(jìn)行抽象和解釋。假設(shè)有n個(gè)人圍成一圈,從某個(gè)人開始,按順時(shí)針方向逐一編號(hào)。接著從編號(hào)為1的人開始報(bào)數(shù),每數(shù)到m就將該人從圈中排除,然后從下一個(gè)人重新開始報(bào)數(shù),直到圈中只剩下一個(gè)人。最后剩下的這個(gè)人的位置就是從1開始數(shù)的序號(hào)。這個(gè)問題可以用數(shù)學(xué)公式或偽代碼進(jìn)行求解。
約瑟夫原理在理論計(jì)算機(jī)科學(xué)、組合數(shù)學(xué)、離散數(shù)學(xué)等領(lǐng)域有著廣泛的應(yīng)用。它不僅是一個(gè)有趣的問題,還可以用于解決各種實(shí)際問題,如任務(wù)調(diào)度、資源管理、網(wǎng)絡(luò)通信等。通過研究和應(yīng)用約瑟夫原理,可以更好地理解和解決一些復(fù)雜的計(jì)算和決策問題。
完整代碼
博主使用C#語言比較多,因此下面是以C#代碼進(jìn)行實(shí)現(xiàn),感興趣的小伙伴可以使用其他語言實(shí)現(xiàn)
主要代碼
string myName = "小美"; string positionValue = "北方人"; string sex = "女生"; List<string> tempList = new List<string>(); string pokerStr = @"黑桃-A、黑桃-2、黑桃-3、黑桃-4、黑桃-5、黑桃-6、黑桃-7、黑桃-8、黑桃-9、黑桃-10、黑桃-J、黑桃-Q、黑桃-K、 紅桃-A、紅桃-2、紅桃-3、紅桃-4、紅桃-5、紅桃-6、紅桃-7、紅桃-8、紅桃-9、紅桃-10、紅桃-J、紅桃-Q、紅桃-K、 梅花-A、梅花-2、梅花-3、梅花-4、梅花-5、梅花-6、梅花-7、梅花-8、梅花-9、梅花-10、梅花-J、梅花-Q、梅花-K、 方塊-A、方塊-2、方塊-3、方塊-4、方塊-5、方塊-6、方塊-7、方塊-8、方塊-9、方塊-10、方塊-J、方塊-Q、方塊-K"; List<string> pokerList = pokerStr.Split('、').Select(a=>a.Replace("\r\n","").Trim()).ToList(); // 隨機(jī)四張 List<string> myPokerList = new List<string>(); for(int i = 0; i < 4; i++) { int randomIndex = new Random().Next(0, pokerList.Count); string pokder = pokerList[randomIndex]; myPokerList.Add(pokder); pokerList.RemoveAt(randomIndex); } // 撕開2半 List<string> newList = new List<string>(); tempList = NewOrder(myPokerList); // 右半邊 for (int i = 0; i < tempList.Count; i++) { newList.Add($"{tempList[i]}-右"); } // 左半邊 for (int i = 0; i < tempList.Count; i++) { newList.Add($"{tempList[i]}-左"); } // 姓名數(shù)字 - 動(dòng)態(tài)值 int myNameLength = myName.Length; string nameString = string.Empty; List<string> nameList = new List<string>(); List<string> nameListMy = new List<string>(); for (int i = 0; i < myNameLength; i++) { string pv = newList[newList.Count - 1 - i]; nameListMy.Add(pv); nameString = $"{pv},{nameString}"; } for (int i = 0; i < newList.Count - myNameLength; i++) { nameString += $",{newList[i]}"; } nameList = nameString.Split(',').Where(a => !string.IsNullOrWhiteSpace(a)).ToList(); // 拿起最上面3張牌,插入到任意牌的中間,1、2、3、4、5,1到5之間插入 int topValue = 3; int randomValue = new Random().Next(1, nameList.Count - topValue - 1); List<string> top3List = new List<string>(); for (int i = 0; i < nameList.Count - topValue; i++) { if (i == randomValue) { for (int j = topValue; j > 0; j--) { top3List.Add(nameList[nameList.Count - j]); } } top3List.Add(nameList[i]); } // 【第一次移除牌】 // 拿起最上面一張牌,就是黑桃3-右,放到一遍 string topPoker = top3List[top3List.Count - 1]; List<string> sevenList = new List<string>(); for (int i = 0; i < top3List.Count - 1; i++) { sevenList.Add(top3List[i]); } // 南方人:拿起一張牌、北方人:拿起兩張牌、不知道是南方還是北方人:拿起三張牌 // 插入到剩下牌的中間,1到5或者1到6之間插入 int fromValue = (positionValue == "南方人" ? 1 : positionValue == "北方人" ? 2 : 3); int randomValue2 = new Random().Next(1, sevenList.Count - fromValue - 1); List<string> fromList = new List<string>(); for (int i = 0; i < sevenList.Count - fromValue; i++) { if (i == randomValue2) { for (int j = fromValue; j > 0; j--) { fromList.Add(sevenList[sevenList.Count - j]); } } fromList.Add(sevenList[i]); } // 【再次移除牌】 // 男生:拿起一張、女生:拿起兩張、拿起的牌,撒到空中去,就是從數(shù)組中移除 int sexType = (sex == "男生" ? 1 : 2); List<string> sexList = new List<string>(); for (int i = 0; i < fromList.Count - sexType; i++) { sexList.Add(fromList[i]); } // 見證奇跡的時(shí)刻 string textValue = "見證奇跡的時(shí)刻"; List<string> forList = new List<string>(); for (int i = 0; i < sexList.Count; i++) { forList.Add(sexList[i]); } for (int i = 0; i < textValue.Length; i++) { string lastElement = forList[forList.Count - 1]; // 保存最后一個(gè)元素的值 forList.RemoveAt(forList.Count - 1); // 移除最后一個(gè)元素 forList.Insert(0, lastElement); // 將最后一個(gè)元素插入到第一個(gè)位置 } // 最后一個(gè)環(huán)節(jié):好運(yùn)留下來,煩惱丟出去 // 第一張放到下面,然后最上面的丟出去,就是移除數(shù)組最后一個(gè) List<string> lastList = new List<string>(); for (int i = 0; i < forList.Count; i++) { lastList.Add(forList[i]); } while (lastList.Count > 1) { string lastElement = lastList[lastList.Count - 1]; // 保存最后一個(gè)元素的值 lastList.RemoveAt(lastList.Count - 1); // 移除最后一個(gè)元素 lastList.Insert(0, lastElement); // 將最后一個(gè)元素插入到第一個(gè)位置 lastList.RemoveAt(lastList.Count - 1); } string lastPoker = lastList[0]; // 合并撲克牌 string allPoker = $"【{lastPoker}】【{topPoker}】";
打亂順序
private List<string> NewOrder(List<string> list) { Random random = new Random(); for (int i = list.Count - 1; i > 0; i--) { int j = random.Next(i + 1); string temp = list[i]; list[i] = list[j]; list[j] = temp; } return list; }
到此這篇關(guān)于C#使用代碼實(shí)現(xiàn)春晚撲克牌魔術(shù)的文章就介紹到這了,更多相關(guān)C#撲克牌魔術(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
快速學(xué)習(xí)C# 設(shè)計(jì)模式之職責(zé)鏈模式
這篇文章主要介紹了C# 設(shè)計(jì)模式之職責(zé)鏈模式的的相關(guān)資料,文中代碼非常細(xì)致,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06C#中DateTime.Compare()比較時(shí)間大小
本文主要介紹了C#中DateTime.Compare()比較時(shí)間大小,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04C#中如何使用Winform實(shí)現(xiàn)炫酷的透明動(dòng)畫界面
這篇文章講解了如何使用Winform實(shí)現(xiàn)炫酷的透明動(dòng)畫界面,Winform相對(duì)于Wpf使用更簡(jiǎn)單一些,系統(tǒng)要求更低,需要了解的朋友可以參考下2015-07-07C#調(diào)用RabbitMQ實(shí)現(xiàn)消息隊(duì)列的示例代碼
這篇文章主要介紹了C#調(diào)用RabbitMQ實(shí)現(xiàn)消息隊(duì)列的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12C#實(shí)現(xiàn)Windows Form調(diào)用R進(jìn)行繪圖與顯示的方法
眾所周知R軟件功能非常強(qiáng)大,可以很好的進(jìn)行各類統(tǒng)計(jì),并能輸出圖形。下面介紹一種R語言和C#進(jìn)行通信的方法,并將R繪圖結(jié)果顯示到WinForm UI界面上的方法,文中介紹的很詳細(xì),需要的朋友可以參考下。2017-02-02c# winform窗口一直置頂顯示在桌面最上方或最底層的方法
winform窗口一直置頂顯示在桌面最上方,這樣的功能真的很實(shí)用的,很多的軟件窗口都有這樣的功能,本文也來實(shí)現(xiàn)一個(gè),感興趣的你千萬不要錯(cuò)過了,希望本文對(duì)你有所幫助2013-01-01C#獲取機(jī)器碼的方法詳解(機(jī)器名,CPU編號(hào),硬盤編號(hào),網(wǎng)卡mac等)
這篇文章主要介紹了C#獲取機(jī)器碼的方法,結(jié)合實(shí)例形式詳細(xì)分析了C#獲取硬件機(jī)器名、CPU編號(hào)、硬盤編號(hào)、網(wǎng)卡mac等信息的相關(guān)實(shí)現(xiàn)方法,需要的朋友可以參考下2016-07-07