鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所

Slides:



Advertisements
Similar presentations
從一付卜克牌 (52 張 ) 中,任選 5 張牌,有幾種組合? 《一對》兩張相同數字的牌和三張不同數字的牌所組成 。 《兩對》有兩對兩張相同數字的牌和一張不同數字的牌所 組成。 《三條》由三張相同數字的牌和兩張不同數字的牌所組成。 《順子》連續性的五張牌所構成的牌型。含有A的五張連 續牌,A必須為首或居末位,才算是順子。
Advertisements

第一單元 建立java 程式.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
計算機程式語言實習課.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
四資二甲 第三週作業 物件導向程式設計.
C#程序设计案例教程 第3章 程 序 结 构.
第 5 章 流程控制 (一): 條件分支.
TQC+ JAVA全國教師研習會 PLWeb 程式設計練習平台 簡介.
第三章 控制结构.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Chapter 5 迴圈.
程式設計實作.
第5章 异常处理 王德俊 上海交通大学继续教育学院.
程式語言的基礎 Input Output Program 世代 程式語言 第一世代 Machine language 第二世代
程式設計概論 1.1 程式設計概論 程式語言的演進 物件導向程式 程式開發流程 1.2 C++開發工具
控制流程 邏輯判斷 迴圈控制.
Java簡介.
第二章 C# 基础知识.
第1章 認識Arduino.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
C#程序设计基础 $3 成员、变量和常量.
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
類別(class) 類別class與物件object.
ASP.NET基本設計與操作 建國科技大學 資管系 饒瑞佶 2007年.
第三章 流程控制與例外處理 資訊教育研究室 製作 注意:本投影片僅供上課使用,非經同意,請勿散播或轉載。
安裝JDK 安裝Eclipse Eclipse 中文化
Methods 靜宜大學資工系 蔡奇偉副教授 ©2011.
C#面向对象程序设计 $7 继承和多态性.
視窗程式設計 (Windows Programming)
檔案讀寫與例外處理 (File IO and Exception Handling)
Java 程式設計 講師:FrankLin.
JAVA 程式設計與資料結構 第四章 陣列、字串與數學物件.
C#程序设计基础 第二章 数据类型.
程式設計實習課(四) ----C 函數運用----
類別與物件 I (Classes and Objects I)
第一單元 建立java 程式.
標籤、按鈕、工具列、狀態列 (Labels, Buttons, Tool Strips, and Status Strips)
撲克牌的整數倍.
第三章 C# 基础知识.
分支宣告與程式設計 黃聰明 國立臺灣師範大學數學系
選擇性結構 if-else… switch-case 重複性結構 while… do-while… for…
JAVA 程式設計 資訊管理系 - 網路組.
程式結構&語法.
4 條件選擇 4.1 程式基本結構 循序式結構 選擇式結構 重複式結構 4-3
C#程序设计基础 $3 成员、变量和常量.
類別與方法 (Classes and Methods)
CH05. 選擇敘述.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
緩衝區溢位攻擊 學生:A 羅以豪 教授:梁明章
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
函數應用(二)與自定函數.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
選擇性結構 if-else… switch-case 重複性結構 while… do-while… for…
PPT注意事项: 当前PPT课件文件必须和提供的源代码文件夹“代码”在同一目录中即不要移动文件夹“代码”的默认位置。
C#快速導讀 流程控制.
判斷(選擇性敘述) if if else else if 條件運算子.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
第二章 Java基本语法 讲师:复凡.
微 處 理 機 專 題 – 8051 C語言程式設計 主題:階乘計算
方法(Method) 函數.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
InputStreamReader Console Scanner
Presentation transcript:

鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所 程式規劃與函式導向程式設計 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

綱要 逐步細分法開發程式及函式導向二十一點模擬程式0.0.1版 函式導向二十一點模擬程式0.0.2版 函式導向二十一點模擬程式0.1版

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

計算機程式設計與作文 以程式語言指示計算機工作 說明文敘述事物內容 同樣以簡潔明確為目標

作文技巧與程式設計 構思立意( 解法構想 ) 布局謀篇( 模組規畫 ) 度句裁章( 流程設計 ) 用字遣詞( 變數運算 )

兒童作文缺失 (1/2) 低年級小朋友. . . 尚無法完整表達意念、掌握形式結構,內容常跳躍,不能聯貫主題。 低年級階段的寫作以「我手寫我口」為主要訓練方式. . . 剛升上中年級的小朋友,在遣詞造句上易流於口語化,甚至常重複出現相同的語詞、句型. . . *蘇國書、林瑋,國語日報兒童園地版兒童寫作觀察報告(九十七年一月到六月) 三之二, 國語日報, 中華民國97年9月18日

兒童作文缺失 (2/2) 大部份的小朋友都是想到什麼就寫什麼,往往把主要內容全寫進某一個段落。這樣一來,不僅段落雜亂,整篇文章也缺乏分段的概念。 高年級學生. . .需要加強的,則是大多慣用條列式思考,缺少在每一條列下衍生細膩描述的能力 *蘇國書、林瑋,國語日報兒童園地版兒童寫作觀察報告(九十七年一月到六月) 三之二, 國語日報, 中華民國97年9月18日

虛擬程式設計 程式設計 就解決問題而言,高階程式語言仍是過細 必須先想好問題解法,才能轉成程式 二進位碼 組合語言 程式語言 就解決問題而言,高階程式語言仍是過細 必須先想好問題解法,才能轉成程式 問題解法先區分模組,寫成演算法,才容易寫成程式 模組區分與演算法構思:虛擬程式設計

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

範例:二十一點 維基百科 http://zh.wikipedia.org/w/index.php?title=Image:Blackjack_game_example.JPG&variant=zh-tw

流程 下注 向各家派發一張明牌 向各家派發一張暗牌 莊家明牌是10 或 A , 詢問閒家是否購買保險 莊家打開暗牌 閒家加註、分牌等動作 莊家逐位詢問閒家是否加牌, 直至閒家不加牌或報到, 才詢問下一位閒家, 輪流詢問閒家直至最後一位閒家加牌完成 莊家如不足 17點便需加牌直至超過或等於 17點 對未有爆煲或報到的玩家, 比點數大小, 大者勝, 可得賠金; 如莊家爆煲, 未有爆煲或報到的閒家便可得賠金 回收已使用的牌及打賞 注: 回收已使用的牌必須順序回收。如分開大牌和小牌個別回收等不正常收牌行為,便有出千的可能,並且可能會被請提出異議而被迫離開。 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

點數計算與發牌 A作一點或十一點,2-10作該牌之點數,J、Q、K作十點。 首先由一位玩家作莊家負責發牌,其餘玩家為閒家。閒家會向莊家投下一定注碼,莊家會以順時鐘方向向眾閒家派發一張暗牌(即不掀開的牌),然後向自己派發一張暗,接著莊家會以順時鐘方向向眾閒家派發一張明牌(即掀開的牌),之後又向自己派發一張明牌。 當眾人手上各擁一張暗牌和一張明牌,莊家就以順時鐘方向逐位閒家詢問是否再要牌(以明牌方式派發),玩家此時要計算是否要牌,因為排牌局的最終目的,是要玩家手上擁有的牌總點數達到21點(或最接近又小於21點),然後和莊家比大小。當一位閒家決定不再要牌後,莊家才向下一位閒家詢問是否再要牌。 (按: 在一般賭場的賭局,莊家並不一定是發牌者。) 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

爆煲(超過21點) 若果閒家要牌後,其手上擁有的牌的總點數超過21點,便要揭開手上所擁有的牌,俗稱爆煲,該閒家的注碼會歸莊家。 反之若其手上擁有的牌的總點數不超過21點,該閒家可決定是否繼續要牌。 當最後一位閒家決定不再要牌後,莊家就必須揭開自己所有手上的牌,若總點數少於17點,就必須繼續要牌;如果莊家爆煲的話,便向原來沒有爆煲的閒家,賠出該閒家所投住的同等的注碼。 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

點數決勝與例牌報到 如果莊家最終沒有爆煲的話,原來沒有爆煲的眾閒家便要揭開手上所有的牌,比較點數決定誰勝誰負,點數較大的取勝。(例牌例外,詳見下文閒家例牌先報到和特別規例) 若某閒家例牌,必須向立即莊家揭開手上所有的牌(即俗稱報到),莊家亦必須向該擁有例牌的閒家賠上一定注碼。 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

保險、加註、分牌 當莊家面牌是10或A,閒家可以外加註碼的一半買保險,以賭莊家的二張牌總和是不是21點,如莊家不是21點便沒收保險金,如是21點便以注碼的一倍半賠償。 如閒家首兩張牌點數之和為11點,可以選擇加倍投注,但加註後僅獲發1張牌。有些賭局容許閒家在首兩張牌總和為10點(甚至任何點數)時加註。 如閒家首兩張牌點數相同,可以選擇分牌,並須加註。分出每門的下注金額須與原注相同。若閒家打兩張A分開,則每張A只獲發1張牌,不可再要牌。有些分牌後後不可再分牌。 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

二十一點、同花順、五龍 如果閒家手中的一張暗牌和一張明牌分別是一張A牌(可作11點)和一張十點牌(K、Q、J、10),這副牌叫做二十一點(Black Jack)(屬例牌),該閒家可向莊家報到,莊家須向該閒家賠上1倍注碼。 有些賭場會加設這一賠彩規例,即玩家的牌面是同花的「6、7、8」便可即收3倍的彩金。 如果閒家要牌直至手上有5張牌而又沒有爆煲,這副牌叫做五龍(屬例牌),該閒家可向莊家報到,莊家須向該閒家賠上2倍注碼。 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

莊家食夾棍 若莊家和眾閒家要以點數決勝(各方都沒有出現爆煲的情況),若該閒家和莊家手上所擁有的牌的總點數一樣的話,算莊家取勝,即俗稱莊家食夾棍,該閒家的注碼會歸莊家。 賭場規則,廿一點是沒有食夾棍這規例,如閒家和莊家手上牌總點數一樣的話,是「打和」,閒家可以拿回原先的注碼。 注意:莊家是五龍而閒家的點數比莊家大的話,仍算莊家取勝,不過莊家食夾棍的權利不適用於閒家例牌(Black Jack和五龍)的情況,因為閒家Black Jack和五龍屬於例牌先賠。 維基百科 http://zh.wikipedia.org/w/index.php?title=%E4%BA%8C%E5%8D%81%E4%B8%80%E9%BB%9E &variant=zh-tw#.E5.9F.BA.E6.9C.AC.E7.8E.A9.E6.B3.95

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

兩則做事方法的名言 物有本末,事有始终,知所先後,則近道矣。 《大學》開宗明義 學而不思則罔,思而不學則殆 《荀子》勸學篇 20

80-20定律、要事第一、工程方法 80-20定律 要事第一 (First things first) 工程方法 (Engineering approach) 辨識問題核心 (Core) 訂定優先順序 (Priority) 快速原型製作 (Fast prototyping) 逐步演化 (Evolution) 定時定量研究法

釐清問題 核心關鍵 Nice to have 優先順序 問題簡化 從實例簡化開始嘗試 做中學

工程方法 v 0.1 v 0.2 v 0.3 v 0.2

工程方法 配合實作逐漸熟悉問題,而非開始即要求全面了解 避免過度分析(analysis paralysis) ,及早獲得可用程式 隨版本演進,有系統地完成程式 隨時有產出,增進信心 越重要的部份較早完成,經歷越多測試,較確實可靠穩定;避免後期發現錯誤,須全面改寫程式 越重要的部份越早完成,可依照環境時程的改變,修改後續版本需求,富有彈性

程式目的 使玩家可以與電腦玩二十一點遊戲

二十一點遊戲模擬v0.1流程 產生牌疊 電腦(莊家)向玩家(一人)及本身派發一張明牌 電腦向玩家及本身派發一張明牌 莊家詢問玩家是否加牌, 直至玩家不加牌或報到 莊家如不足 17點便需加牌直至超過或等於 17點 對未有爆煲或報到的玩家, 比點數大小, 大者勝, 如莊家爆煲, 玩家勝

後續版本規劃 v0.2: 區分明牌暗牌 v0.3: 允許下注 v0.4: 處理同花順與五龍 v0.5: 處理保險、加註、分牌

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

測試規劃 規劃程式須能處理的場景實例 增進對問題的確實了解 有助對程式架構與流程的思考 做為各版本程式紙筆測試、偵錯、驗證的依據 場景實例規畫由簡到繁 輸入錯誤資料等例外狀況可延後規劃 程式的功能與強健(robustness)程度,隨版本演進與通過的測試場景增加而成長

二十一點遊戲模擬原型之測試規劃: 場景1 玩家 莊家 ♠A ♥J ♦10 勝

二十一點遊戲模擬原型之測試規劃: 場景2 玩家 莊家 ♣3 ♥J ♠10 ♦ A勝

二十一點遊戲模擬原型之測試規劃: 場景3 玩家 莊家 ♠8 ♥J ♦7 ♠2 ♣6勝

二十一點遊戲模擬原型之測試規劃: 場景4 玩家 莊家 ♠8 ♥5 ♦8 ♠9 ♣6爆

二十一點遊戲模擬原型之測試規劃: 場景5 玩家 莊家 ♠8 ♥5 ♦8停 ♠9 ♣6勝

二十一點遊戲模擬原型之測試規劃: 場景6 玩家 莊家 ♠8 ♥5 ♦8停 ♠9 ♣8爆

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

一些良好的程式設計習慣 使用研發日誌 夥伴合作程式設計 漸增式與回合式發展 可持續之發展步調 維護原始碼、註解、重要程式文件 依發展階段提昇程式的強健度 可持續之發展步調 維護原始碼、註解、重要程式文件

綱要 作文與程式設計 範例:決勝21點 版本規劃 測試規劃 一些良好的程式設計習慣 測試驅動之程式開發及函式導向二十一點模擬程式0.0.0版

測試 軟體錯誤代價可能極為高昂 測試可以發現並改正程式錯誤 通過的測試越多, 程式越可靠

測試時機與風險

極端化程式設計 (Extreme Programming) 撰寫一個簡單的測試程式 編譯執行程式,看它失敗 增添恰能通過測試之程式碼並編譯偵錯 重整及消除冗餘程式碼並編譯偵錯 反覆進行上述步驟

測試驅動開發方式 (Test-Driven Development -TDD) 寫測試主程式, 呼叫測試函式, 建置, 看它失敗 寫測試類別, 加上測試函式, 重新建置, 看它失敗 撰寫欲測試的類別, 加上株段(stub)函式, 使通過建置, 但不通過測試 改寫株段函式, 通過建置與測試 以相同方式, 增加新測試

BlackJack_0_0_0.Program using System; using System.Diagnostics; namespace BlackJack_0_0_0 { class Program { static void Main(string[] args) { Debug.Assert(TestScenario_1_OK()); } static bool TestScenario_1_OK() { return true;

株段(Stub)函式 沒有實質內容 只是傳回true的值,使測試可以通過 可以驗證函式的呼叫與宣告是否一致,並建立程式的初步架構

綱要 逐步細分法開發程式及函式導向二十一點模擬程式0.0.1版 函式導向二十一點模擬程式0.0.2版 函式導向二十一點模擬程式0.1版

Stepwise Refinement 演算法設計 Magic number 7 加減 2 10個或更少的主要步驟或虛擬碼 逐層分解工作 各項工作依繁簡、重複性、可替代性決定是否寫為函式 以函式模組為組成單位 函式導向程式設計

虛擬碼TestScenario_1_OK() 設莊家手牌第一張為♥J 設玩家手牌第二張為♦10 傳回判斷玩家手牌總點數為21點之真偽

虛擬碼IsBlackJack(玩家手牌兩張) 合計手牌總點數(A為1點,2為2點,…,10及J、Q、K為10點) isBlackJack = 總點數為21之真偽 if !isBlackJack且第一張牌為A, 3.1 第一張牌點數改為11 3.2 isBlackJack = 總點數為21之真偽 if !isBlackJack且第二張牌為A, 4.1 第二張牌點數改為11 4.2 isBlackJack = 總點數為21之真偽 return isBlackJack

虛擬碼Points(牌點rank) points = rank; if (rank > 10) points = 10 return points

程式結構圖(Structure Chart) TestScenario_1_OK() IsBlackJack() Points()

由上而下與由下而上 由上而下 由下而上 上下夾擊 假設底層類別已經存在 較易決定下層類別規格 主要之呼叫介面測試較多 先實作並測試底層類別(單元測試Unit Test) 較易測試類別實體 上下夾擊 開始時已有一批類別可用 51

BlackJack_0_0_1.Suit enum Suit { CLUB = 0, DIAMOND = 1, HEART = 2, SPADE = 3 }

BlackJack_0_0_1.Program. TestScenario_1_OK Suit[] humanPlayerHandSuit = new Suit[2]; int[] humanPlayerHandRank = new int[2]; Suit[] computerPlayerHandSuit = new Suit[1]; int[] computerPlayerHandRank = new int[1]; humanPlayerHandSuit[0] = Suit.SPADE; humanPlayerHandRank[0] = 1; computerPlayerHandSuit[0] = Suit.HEART; computerPlayerHandRank[0] = 11; humanPlayerHandSuit[1] = Suit.DIAMOND; humanPlayerHandRank[1] = 10; return IsBlackJack(humanPlayerHandRank);

BlackJack_0_0_1.Program. IsBlackJack int point0 = Points(handRank[0]); int point1 = Points(handRank[1]); bool isBlackJack =(point0 + point1 == 21); if (!isBlackJack && point0 == 1) { point0 = 11; isBlackJack = (point0 + point1 == 21); } if (!isBlackJack && point1 == 1) { point1 = 11; return isBlackJack;

BlackJack_0_0_1.Program.Points static int Points(int rank) { int points = rank; if (rank > 10) points = 10; return points; }

程式擴充與彈性的考慮 目的只是要完成場景1的測試 不考慮太多程式的擴充與彈性 符合TDD 每一個版本,都做一些修改,提昇程式功能與彈性 一段時日之後,所完成的程式功能就很可觀 穩紮穩打 比一開始就考慮全部功能的程式設計方法可靠

綱要 逐步細分法開發程式及函式導向二十一點模擬程式0.0.1版 函式導向二十一點模擬程式0.0.2版 函式導向二十一點模擬程式0.1版

程式BlackJack_0_0_2目標及假設 目標:掌握所有六個測試場景 假設每個場景 都有一付牌疊 (deck) 藉由發牌(deal a card)建立玩家與莊家的手牌(hand) 玩家與莊家最後的狀態(21點(BlackJack)、爆煲(Burst)、或二者皆不是(Pass)) 要分別檢驗決定

虛擬碼TestScenario_1_OK() 建立牌疊(♠A、♥J、♦10) 發一張牌給玩家 發一張牌給莊家 取得玩家狀態 取得莊家狀態 return 玩家狀態為BlackJack且莊家狀態為Pass之真偽

虛擬碼 GetStatus(手牌, out sum) sum =手牌總點數(A為1點) status=由總點數判斷出的狀態(BlackJack、Burst、Pass) if (status != Pass) return status if(手牌中有A) total=sum+10 if(總點數==21) status = BlackJack if(total <= 21) sum = total return status

程式結構圖(Structure Chart) TestScenario_1_OK PrepareDeck_1 DealACard GetStatus Point JudgeStatus

BlackJack_0_0_2.Program. Status enum Status { PASS = 0, BLACK_JACK = 1, BURST = 2 }

BlackJack_0_0_2.Program. Main片段 Debug.Assert(TestScenario_1_OK(), "Scenario 1 test failed"); Debug.Assert(TestScenario_2_OK(), "Scenario 2 test failed"); Debug.Assert(TestScenario_3_OK(), "Scenario 3 test failed"); Debug.Assert(TestScenario_4_OK(), "Scenario 4 test failed"); Debug.Assert(TestScenario_5_OK(), "Scenario 5 test failed"); Debug.Assert(TestScenario_6_OK(), "Scenario 6 test failed");

BlackJack_0_0_2.Program. TestScenario_1_OK片段 (1/2) Suit[] deckSuit = new Suit[3]; int[] deckRank = new int[3]; PrepareDeck_1(deckSuit, deckRank, out top); Suit[] humanPlayerHandSuit = new Suit[2]; int[] humanPlayerHandRank = new int[2]; int nHumanPlayerHandCards = 0; Suit[] computerPlayerHandSuit = new Suit[1]; int[] computerPlayerHandRank = new int[1]; int nComputerPlayerHandCards = 0; DealACard(deckSuit, deckRank, ref top, humanPlayerHandSuit, humanPlayerHandRank, ref nHumanPlayerHandCards);

BlackJack_0_0_2.Program. TestScenario_1_OK片段 (2/2) Status humanPlayerStatus = GetStatus(humanPlayerHandRank, nHumanPlayerHandCards, out humanPlayerTotalPoints); Status computerPlayerStatus = GetStatus(computerPlayerHandRank, nComputerPlayerHandCards, out computerPlayerTotalPoints); return (humanPlayerStatus == Status.BLACK_JACK && computerPlayerStatus == Status.PASS);

BlackJack_0_0_2.Program. PrepareDeck_1 static void PrepareDeck_1(Suit[] deckSuit, int[] deckRank, out int top) { deckSuit[0] = Suit.SPADE; deckRank[0] = 1; deckSuit[1] = Suit.HEART; deckRank[1] = 11; deckSuit[2] = Suit.DIAMOND; deckRank[2] = 10; top = 0; }

BlackJack_0_0_2.Program. DealACard static void DealACard(Suit[] deckSuit, int[] deckRank, ref int top, Suit[] suit, int[] rank, ref int nCards) { suit[nCards] = deckSuit[top]; rank[nCards] = deckRank[top]; ++top; ++nCards; }

BlackJack_0_0_2.Program. GetStatus片段 (1/3) int[] point = new int[nCards]; int sum = 0; for (i = 0; i < nCards; ++i) { point[i] = Point(handRank[i]); sum += point[i]; } Status status = JudgeStatus(sum); totalPoints = sum; if (status != Status.PASS) return status; bool isWithAce = false;

BlackJack_0_0_2.Program. GetStatus片段 (2/3) for (i = 0; i < nCards; ++i) { if (point[i] == 1) { isWithAce = true; break; } if (isWithAce) { sum += 10; if (sum == 21) { status = Status.BLACK_JACK;

BlackJack_0_0_2.Program. GetStatus片段 (3/3) if (sum <= 21) { totalPoints = sum; } // keep original totalPoints if sum > 21 return status;

BlackJack_0_0_2.Program. JudgeStatus static Status JudgeStatus(int sum) { Status status; if (sum == 21) { status = Status.BLACK_JACK; } else if (sum > 21) { status = Status.BURST; } else { status = Status.PASS; } return status;

綱要 逐步細分法開發程式及函式導向二十一點模擬程式0.0.1版 函式導向二十一點模擬程式0.0.2版 函式導向二十一點模擬程式0.1版

二十一點遊戲模擬v0.1流程 產生牌疊 電腦(莊家)向玩家(一人)及本身派發一張明牌 電腦向玩家及本身派發一張明牌 莊家詢問玩家是否加牌, 直至玩家不加牌或報到 莊家如不足 17點便需加牌直至超過或等於 17點 對未有爆煲或報到的玩家, 比點數大小, 大者勝, 如莊家爆煲, 玩家勝 73

二十一點遊戲模擬v0.1流程虛擬碼 虛擬碼RunGame() 1 建立牌疊 2 向玩家及電腦各發一張牌 3 向玩家及電腦各發一張牌 1 建立牌疊 2 向玩家及電腦各發一張牌 3 向玩家及電腦各發一張牌 4 取得玩家及電腦手牌狀態,並作處理 5 while(玩家要牌且牌疊仍有牌) 5.1 發一張牌給玩家 5.2 取得玩家手牌狀態,並作處理 6 while(電腦要牌且牌疊仍有牌) 6.1 發一張牌給電腦 6.2 取得電腦手牌狀態,並作處理 7 計點分勝負

撲克牌與數字的對應 1 2 3 4 5 6 7 8 9 10 11 12 ♣A ♣2 ♣3 ♣4 ♣5 ♣6 ♣7 ♣8 ♣9 ♣10 ♣J 1 2 3 4 5 6 7 8 9 10 11 12 ♣A ♣2 ♣3 ♣4 ♣5 ♣6 ♣7 ♣8 ♣9 ♣10 ♣J ♣Q ♣K 13 14 15 16 17 18 19 20 21 22 23 24 25 ♦A ♦2 ♦3 ♦4 ♦5 ♦6 ♦7 ♦8 ♦9 ♦10 ♦J ♦Q ♦K 26 27 28 29 30 31 32 33 34 35 36 37 38 ♥A ♥2 ♥3 ♥4 ♥5 ♥6 ♥7 ♥8 ♥9 ♥10 ♥J ♥Q ♥K 39 40 41 42 43 44 45 46 47 48 49 50 51 ♠A ♠2 ♠3 ♠4 ♠5 ♠6 ♠7 ♠8 ♠9 ♠10 ♠J ♠Q ♠K

準備牌疊虛擬碼 虛擬碼PrepareDeck(牌疊) 1 陣列used[0~51] (標記某張牌是否已被用過)均設為偽 2 for(i=0; i<52; ++i) 2.1 隨機產生一個0~51的整數,令為pos 2.2 while(used[pos]為真) 2.2.1 ++pos 2.2.2 pos 設為 pos 除以 52的餘數 (以免pos超出範圍) 2.3 牌疊第i張牌之花色由pos除以4的商決定 2.4 牌疊第i張牌之牌點由pos除以4的餘數加1決定 2.5 設used[pos]為真

程式結構圖 RunGame DealACard GetStatus DumpHand JudgeStatus Point PrepareDeck DealACard GetStatus DumpHand IsBlackJackOrBurst ComputerPlayerWants OneMoreCard HumanPlayerWants OneMoreCard JudgeStatus Point

BlackJack_0_1.Program. Main static void Main(string[] args) { Debug.Assert(TestDeck_OK(), "Deck test failed"); Console.WriteLine(); Console.WriteLine("21點遊戲開始"); RunGame(); }

BlackJack_0_1.Program. TestDeck片段 (1/4) Suit[] deckSuit = new Suit[52]; int[] deckRank = new int[52]; PrepareDeck(deckSuit, deckRank, out top); Suit[] cardsSuit = new Suit[52]; int[] cardsRank = new int[52]; int nCards = 0; for (i = 0; i < 52; ++i) { DealACard(deckSuit, deckRank, ref top, cardsSuit, cardsRank, ref nCards); }

BlackJack_0_1.Program. TestDeck片段 (2/4) int[] nSuit = new int[4]; for (s = 0; s < 4; ++s) { nSuit[s] = 0; } int[] nRank = new int[13]; for (r = 0; r < 13; ++r) { nRank[r] = 0;

BlackJack_0_1.Program. TestDeck片段 (3/4) for (i = 0; i < 52; ++i) { switch (cardsSuit[i]) { case Suit.CLUB: nSuit[0]++; break; case Suit.DIAMOND: nSuit[1]++; break; case Suit.HEART: nSuit[2]++; break; case Suit.SPADE: nSuit[3]++; break; default: break; } nRank[cardsRank[i] - 1]++;

BlackJack_0_1.Program. TestDeck片段 (4/4) bool suit_OK = true; for (s = 0; s < 4; s++) { if (nSuit[s] != 13) { suit_OK = false; break; } bool rank_OK = true; for (r = 0; r < 13; r++) { if (nRank[r] != 4) { rank_OK = false; break; return suit_OK && rank_OK;

BlackJack_0_1.Program. RunGame片段 Suit[] deckSuit = new Suit[52]; int[] deckRank = new int[52]; PrepareDeck(deckSuit, deckRank, out top); // a player can have at most 11 cards // {A, A, A, A, 2, 2, 2, 2, 3, 3, 3} // for not burst or BlackJack Suit[] humanPlayerHandSuit = new Suit[11]; int[] humanPlayerHandRank = new int[11]; int nHumanPlayerHandCards = 0; Suit[] computerPlayerHandSuit = new Suit[11]; int[] computerPlayerHandRank = new int[11]; int nComputerPlayerHandCards = 0;

BlackJack_0_1.Program. RunGame片段 (1/4) DealACard(deckSuit, deckRank, ref top, humanPlayerHandSuit, humanPlayerHandRank, ref nHumanPlayerHandCards); Status humanPlayerStatus = GetStatus(humanPlayerHandRank, nHumanPlayerHandCards, out humanPlayerTotalPoints); DumpHand("玩家", humanPlayerHandSuit, humanPlayerHandRank, nHumanPlayerHandCards, humanPlayerTotalPoints); if (IsBlackJackOrBurst(humanPlayerStatus)) return;

BlackJack_0_1.Program. RunGame片段 (2/4) DealACard(deckSuit, deckRank, ref top, computerPlayerHandSuit, computerPlayerHandRank, ref nComputerPlayerHandCards); Status computerPlayerStatus = GetStatus(computerPlayerHandRank, nComputerPlayerHandCards, out computerPlayerTotalPoints); DumpHand("電腦", computerPlayerHandSuit, computerPlayerHandRank, nComputerPlayerHandCards, computerPlayerTotalPoints); if(IsBlackJackOrBurst(computerPlayerStatus)) return;

BlackJack_0_1.Program. RunGame片段 (3/4) while (humanPlayerStatus == Status.PASS && HumanPlayerWantsOneMoreCard() && top < 51){ DealACard(deckSuit, deckRank, ref top, humanPlayerHandSuit, humanPlayerHandRank, ref nHumanPlayerHandCards); humanPlayerStatus = GetStatus(humanPlayerHandRank, nHumanPlayerHandCards, out humanPlayerTotalPoints); DumpHand("玩家", humanPlayerHandSuit, humanPlayerHandRank, nHumanPlayerHandCards, humanPlayerTotalPoints); if (IsBlackJackOrBurst(humanPlayerStatus)) return; }

BlackJack_0_1.Program. RunGame片段 (4/4) if (computerPlayerTotalPoints >= humanPlayerTotalPoints) { Console.WriteLine("電腦勝"); } else { Console.WriteLine("玩家勝"); }

BlackJack_0_1.Program. PrepareDeck片段 (1/2) Random rand = new Random(); bool[] used = new bool[52]; for (i = 0; i < 52; ++i) { used[i] = false; } pos = rand.Next() % 52; while (used[pos]) { ++pos; pos = pos % 52; s = pos / 13;

BlackJack_0_1.Program. PrepareDeck片段 (2/2) switch (s) { case 0: deckSuit[i] = Suit.CLUB; break; case 1: deckSuit[i] = Suit.DIAMOND; break; case 2: deckSuit[i] = Suit.HEART; break; case 3: deckSuit[i] = Suit.SPADE; break; default: break; } deckRank[i] = pos % 13 + 1; used[pos] = true; top = 0;

BlackJack_0_1.Program. HumanPlayerWantsOneMoreCard static bool HumanPlayerWantsOneMoreCard() { Console.Write("要再一張牌嗎? (y/n) "); string answer = Console.ReadLine(); return (answer == "Y" || answer == "y"); }

BlackJack_0_1.Program. ComputerPlayerWantsOneMoreCard static bool ComputerPlayerWantsOneMoreCard(int totalPoints) { return (totalPoints < 17); }

BlackJack_0_1.Program.DumpCard片段 string[] ranks = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; switch (suit) { case Suit.CLUB: Console.Write("c" + ranks[rank - 1]); break; case Suit.DIAMOND: Console.Write("d" + ranks[rank - 1]); case Suit.HEART: Console.Write("h" + ranks[rank - 1]); . . . . . . }