房琛琛,齊 琪,陳 龍,薄鈞戈
(西安交通大學(xué) 計算機科學(xué)與技術(shù)學(xué)院,陜西 西安 710049)
“新工科”改革的目標是培養(yǎng)工業(yè)4.0背景下的復(fù)合型人才,課程教學(xué)模式必須適應(yīng)產(chǎn)業(yè)發(fā)展需求而提高學(xué)生解決復(fù)雜工程問題的能力,而實驗教學(xué)是培養(yǎng)學(xué)生實踐動手能力和創(chuàng)新能力的重要途徑[1-2]。但以往的實驗教學(xué)只以知識傳授為重點,缺少對學(xué)生專業(yè)特點、學(xué)生興趣及掌握情況的考慮,導(dǎo)致實驗教學(xué)效果不理想[3]。
該文以C#程序設(shè)計課程為例,分析當(dāng)前實驗教學(xué)的現(xiàn)狀,并融合學(xué)生的專業(yè)特點,設(shè)計了一個內(nèi)容貫穿整個課程知識體系的實驗案例。該案例以“CDIO (構(gòu)思(Conceive)、設(shè)計(Design)、實現(xiàn)(Implement)和運作(Operate))”理念[4]為導(dǎo)向,以學(xué)生喜歡的游戲主題為切入點,引導(dǎo)學(xué)生從面向過程到面向?qū)ο蟮某绦蛟O(shè)計思維過渡,倡導(dǎo)學(xué)生通過“理論學(xué)習(xí)、方法引導(dǎo)、自主研學(xué)”的方式來完成綜合性實驗以解決復(fù)雜的工程問題。
目前C#程序設(shè)計實驗教學(xué)主要存在以下幾個問題[5-8]:
(1)教學(xué)內(nèi)容抽象、知識點多和雜。C#是一種面向?qū)ο蟮恼Z言,內(nèi)容包括類、對象、繼承、多態(tài)等。這些知識點比較抽象,學(xué)生難以理解,導(dǎo)致失去學(xué)習(xí)興趣。
(2)傳統(tǒng)的實驗案例無法將知識點貫通。大多數(shù)實驗案例都是與當(dāng)周理論課知識點相對應(yīng)的實驗,各個實驗之間的聯(lián)系較少,課程結(jié)束時學(xué)生難以將課程體系的知識點融會貫通,導(dǎo)致遇到實際問題無所適從。
(3)教學(xué)過程和實驗案例脫離實踐應(yīng)用。傳統(tǒng)的教學(xué)注重理論知識的講解,以教師講授為中心,學(xué)生被動接受知識,對學(xué)生實際開發(fā)能力缺乏足夠的重視。與之對應(yīng)的實驗案例也往往是“為了實驗而實驗”,內(nèi)容呆板老套缺乏實踐應(yīng)用價值,難以激發(fā)學(xué)生興趣,導(dǎo)致學(xué)生參與度低。
(4)學(xué)生層次不同,難以個性化培養(yǎng)。上課的學(xué)生所屬專業(yè)不同,對計算機程序掌握的程度也參差不齊。而現(xiàn)階段的教學(xué)內(nèi)容和實驗案例基本都是按照一致的要求來給學(xué)生傳授,這種情況會導(dǎo)致基礎(chǔ)好、能力強的學(xué)生得不到更深層次的學(xué)習(xí),也會使基礎(chǔ)弱、能力弱的學(xué)生沒有很好掌握要點的現(xiàn)象出現(xiàn)。
針對上述問題,可以按照以下幾點特征來選取實驗案例:
(1)實驗案例應(yīng)來源于生產(chǎn)生活,最好有些趣味性,對學(xué)生而言不陌生、有代入感,能激發(fā)學(xué)生的學(xué)習(xí)興趣;
(2)實驗案例的綜合性應(yīng)較強,要能夠涵蓋“C#程序設(shè)計”的主要知識點,如類、對象、方法、繼承、多態(tài)等,理論與實踐要有聯(lián)系;
(3)難度適中,有層次性,可拓展性好,可以引入新知識。使不同層次學(xué)生能有所學(xué)、有所思、有所得。并且能夠培養(yǎng)學(xué)生自主學(xué)習(xí)的能力;
(4)可以根據(jù)不同的專業(yè)特點,設(shè)計案例中有不同的側(cè)重點,使學(xué)生發(fā)揮專業(yè)特長,所學(xué)能所用。
實踐教學(xué)是為教學(xué)目標服務(wù)的,為了更好地解決當(dāng)前實踐教學(xué)存在的問題,對現(xiàn)有實踐教學(xué)模式進行改革,該文提出“理論學(xué)習(xí)、方法引導(dǎo)、自主研學(xué)”的實踐教學(xué)模式,具體內(nèi)容如下:
(1)理論學(xué)習(xí)。實踐教學(xué)以現(xiàn)有教材的課程大綱為基礎(chǔ)。需掌握的知識有:
①開發(fā)環(huán)境的使用、程序調(diào)式等基本操作。
②面向?qū)ο蟪绦蛟O(shè)計方法的原理:類與對象、繼承與多態(tài)。
③C#基本語法。
④Windows窗體程序框架、常用控件、鼠標鍵盤事件、繪圖,事件。
⑤文件的讀取、序列化與反序列化知識。
(2)方法引導(dǎo)。學(xué)生在老師引導(dǎo)下,構(gòu)思(Conceive)和設(shè)計(Design)實驗內(nèi)容。以五子棋游戲開發(fā)為目標,分解需求。啟發(fā)學(xué)生發(fā)現(xiàn)已掌握的知識點和分解任務(wù)的解決之間的關(guān)系(見表1)。然后對現(xiàn)有任務(wù)進行算法設(shè)計和數(shù)據(jù)結(jié)構(gòu)定義,進入小組討論。各組根據(jù)任務(wù)的劃分情況制定系統(tǒng)開發(fā)框架和實施方案。
表1 知識模塊和項目應(yīng)用的關(guān)系
(3)自主研學(xué)。實驗中期的創(chuàng)新基本能力培養(yǎng)階段,學(xué)生自主完成(Implement)整個實驗;針對本案例中的復(fù)雜問題,借助網(wǎng)絡(luò)資源調(diào)查研究已有技術(shù)的現(xiàn)狀和常用應(yīng)用方法。通過查閱資料并編程實現(xiàn),既鍛煉了學(xué)生的自主學(xué)習(xí)能力,也鍛煉了學(xué)生的動手實踐能力。
一個好的實驗案例可以讓學(xué)生運用所學(xué)的知識,設(shè)計并編程實現(xiàn)一個復(fù)雜度較高的綜合性項目。通過完成這一項目,能夠讓學(xué)生深刻理解如何通過計算機來解決實際問題的過程,并且提高自己的動手實踐能力。
本實驗案例選擇五子棋,因為五子棋是一個趣味性較強,能夠吸引學(xué)生去自主完成的一個項目,并且其綜合性和層次性也相對較強。五子棋是一種兩人對弈的純策略型游戲,深受大眾喜歡,以此為實踐項目可以提高學(xué)生的學(xué)習(xí)興趣、學(xué)生的積極性和參與度,從而達到寓教于樂的效果;由于整個項目涵蓋程序設(shè)計課程絕大多數(shù)知識點,并且訓(xùn)練學(xué)生的設(shè)計、編碼、調(diào)試、運行等多方面能力,所以在知識點覆蓋和能力訓(xùn)練上都具有較強的綜合性;整個案例中的功能從易到難,由淺入深,學(xué)生可以根據(jù)自己的能力選擇其中部分功能予以實現(xiàn),通過這種層次性及功能性來適應(yīng)不同程度和不同專業(yè)學(xué)生的各種需求[9]。
本案例的主要內(nèi)容是設(shè)計并實現(xiàn)應(yīng)用軟件——五子棋。五子棋是一種兩人對弈的棋類游戲,基于.NET Framework平臺的圖形用戶界面模板,既可以利用C#語言和面向?qū)ο蟮幕驹矸奖愕膶崿F(xiàn),又有很強的趣味性。其中,系統(tǒng)的功能設(shè)計和數(shù)據(jù)結(jié)構(gòu)的設(shè)計是實驗的重點,研究游戲的禁手規(guī)則、人機對戰(zhàn)算法是實驗的難點。通過對五子棋軟件的實現(xiàn),加深學(xué)生對計算機進行問題求解過程的理解,培養(yǎng)學(xué)生將復(fù)雜工程問題進行“抽象、算法、自動化”的能力。
本案例涉及的內(nèi)容包括以下幾方面:
(1)五子棋基本規(guī)則:這部分主要包括落子規(guī)則和判負規(guī)則。根據(jù)是否對限制黑棋先行的優(yōu)勢,將規(guī)則分為無禁手和禁手規(guī)則。
(2)程序設(shè)計基本方法:面向?qū)ο蟮母拍詈退枷?、基?Net Framework的C#圖形用戶界面、GDI+繪圖應(yīng)用、常用的類庫。
(3)案例的不同側(cè)重點:面向理科生的重點為博弈算法,因為有理科背景的學(xué)生擅長運行數(shù)學(xué)模型解決問題。面向工科生的重點是軟件擴展功能的完善,如悔棋、復(fù)盤、登陸/注冊、背景音樂等。
(1)面向理科生的側(cè)重點是博弈算法。讓計算機模仿人類下棋,傳統(tǒng)的算法是采用博弈算法。該算法的數(shù)據(jù)結(jié)構(gòu)就是博弈樹(如圖1所示)。圖中A、B是對弈雙方,A有多種落子的選擇,那么B也就有多種對應(yīng)的下法。博弈樹就是把所有的走法都列出來所構(gòu)成的樹形圖。圖中的每一個節(jié)點表征博弈的一個確定狀態(tài)。通常把從一個節(jié)點通向其子節(jié)點的過程稱為一個行動,每一條邊代表一個動作。這里還有一些名詞,例如,分支因子表示節(jié)點的子節(jié)點數(shù)目;根節(jié)點表征博弈的初始狀態(tài);葉子節(jié)點表示那些沒有子節(jié)點的節(jié)點;用端節(jié)點代表了博弈結(jié)束的狀態(tài),可以通過評估端節(jié)點來判斷博弈的結(jié)果。
圖1 博弈樹
博弈算法中的子節(jié)點是以幾何級數(shù)上升的,算法的任務(wù)是從這些子節(jié)點中找到一個最優(yōu)的節(jié)點,從而贏得游戲。整個算法分為搜索和估值兩部分,對所有可能落子位置進行一個評價就是估值,然后選擇估值最高的位置作為最終落子位置。經(jīng)典的算法有極大極小搜索算法和Alpha-beta剪枝算法。
①極大極小搜索算法(MinMax)。
該策略是指,在假定對手總是執(zhí)行最佳動作的前提下,最大化自己的博弈收益。極小極大策略[10-11]是在各種風(fēng)險最小的策略中選擇收益最大的一個,從而在保證收益的同時最小化了風(fēng)險,但這是以放棄最優(yōu)策略為代價。因此這是一種保守的策略選擇方法。在A和B的兩人博弈中,A嘗試最大化其收益,而B嘗試最小化A的收益[12]。
對于深度為4的博弈樹采用極大極小搜索算法的過程(如圖2所示),要想知道第一步怎么走,就要從第4步開始。根據(jù)樹狀圖的可行路徑,列出每一種可能的分數(shù)。第4步是對手下的,他要做的是最小化分數(shù),于是對手根據(jù)結(jié)果可以反推出如圖2中b標記邊的選擇。繼續(xù)從后往前看到第3步,當(dāng)知道了對手的選擇以后,可以根據(jù)對手的結(jié)果反推出自己的選擇,要做的是最大化這個分數(shù),如圖2中a標記邊的選擇。重復(fù)這個步驟,最終可以發(fā)現(xiàn)第一步的最優(yōu)選擇。
圖2 極大極小算法搜索過程
②Alpha-beta剪枝。
雖然五子棋的標準棋盤是15×15,但整個搜索空間也是一個龐大的數(shù)字。為了限制博弈樹的大小,通常不會對所有的狀態(tài)做展開,并且只訪問那些展開的狀態(tài)。Alpha-beta剪枝算法[13-14]被用來減少博弈樹中被極小極大策略評估的節(jié)點數(shù)目,從而縮減博弈樹的規(guī)模。它的原理是在博弈樹內(nèi)搜索過程中判斷出哪一個葉節(jié)點可以勝出棋局時,其他搜索就不需要繼續(xù)了。即對選取極大極小值進行判斷,剪掉獲勝機率小的葉節(jié)點,從而減小搜索量,提高效率[15]。
剪枝過程如圖3所示。在選擇w3走的時候同時把所得alpha和beta傳遞下去,在經(jīng)過w1->w3->w7得到f(w3)=4(同時使beta=4)后,首先進行判斷:如果alpha>beta,說明這個點一定不會產(chǎn)生最優(yōu)解了,直接返回beta=4,不用再去搜索w8和w9。因為w1在走w2這條路時得到的一個估價值f(w1)就是alpha,而w3是Min層,它會選擇子節(jié)點w7、w8、w9中f的值小的作為f(w3),所以w3在得到f(w3)=4后如果繼續(xù)搜索w8、w9,只能得到比它更小的值;w1是Max層,它要修改的條件就是找到估價值比它更大的子節(jié)點,而w1目前已知的估價值f(w1)=6比f(w3)要大,所以w1不會選w3作為下一步,也就沒有必要繼續(xù)搜索下去。這樣就剪掉了w8、w9這兩個分支,直接跳出w3進入w4繼續(xù)搜索。這樣就實現(xiàn)了有效的剪枝優(yōu)化。
圖3 Alpha-bet剪枝算法過程
(2)面向工科生的側(cè)重點是軟件的擴展功能的完善。擴展功能可以包括以下內(nèi)容:
①悔棋:可以允許玩家一方悔一步或多步棋。
②復(fù)盤:下棋結(jié)束后重現(xiàn)下棋的過程,玩家可以根據(jù)此過程分析和研究成敗得失和著法優(yōu)劣。
③時限:可以通過設(shè)置玩家下棋的時間限制來增加游戲難度。
④登陸/注冊:用戶可以通過注冊來設(shè)置用戶名、密碼等信息,然后通過登陸進入游戲環(huán)節(jié)。
⑤音樂:可以設(shè)置背景音樂或音效使玩家有良好的游戲體驗。
⑥頁面美化:可以添加背景圖片、設(shè)計頁面布局等完成美化功能。
軟件的擴展功能不局限于此,學(xué)生可以根據(jù)自己的能力添加更多的功能。
(1)棋盤和棋子的實現(xiàn)。
棋類游戲最核心的數(shù)據(jù)結(jié)構(gòu)是棋盤,棋盤表示的高效與否決定了棋類游戲的性能。棋盤數(shù)據(jù)結(jié)構(gòu)的設(shè)計應(yīng)該根據(jù)系統(tǒng)的設(shè)計目標而定。可考察棋盤的兩種數(shù)據(jù)結(jié)構(gòu)表示。
①二維數(shù)組。
五子棋棋盤上的任意一個位置都可以用橫縱坐標來確定,因為棋盤是規(guī)則的長方形。這樣就可以用二維數(shù)組直觀清晰地表示棋盤結(jié)構(gòu)。因為行和列的二維信息比較清楚,所以用二維數(shù)組存取數(shù)據(jù)是比較高效的。但是,也有缺點,要確定棋子狀態(tài),需要掃描整個棋盤,也就是遍歷存儲棋盤的二維數(shù)組才能獲得特定棋子的狀態(tài),這點是比較耗時。
②鏈表。
如果用數(shù)組來保存棋子的信息,很難體現(xiàn)出棋子之間的位置(前、后、左、右)關(guān)系,這是因為單個數(shù)組元素保存的信息太少。在系統(tǒng)進行一些復(fù)雜的操作也是十分低效,例如悔棋,該功能是退回到上一步,因此需要知道上一步的下子位置。解決這些問題可以用鏈表結(jié)構(gòu)。因為鏈表可以存儲上個節(jié)點和下個節(jié)點的位置信息,因此可以用鏈表把用戶下子的順序鏈接起來。用鏈表不光可以存儲棋子位置的橫縱坐標值,還可以存儲其他信息,例如,棋子的歸屬信息,即是我方還是敵方的棋子。
(2)繪制棋盤和棋子。
繪制棋盤的過程在private void Form1_Paint(object sender, PaintEventArgs e)事件中利用GDI+繪圖函數(shù)DrawLine()畫出一個棋盤。定義棋子類,包含棋子的坐標、狀態(tài)、評分等信息。
(3)落棋判斷。
首先收集棋盤已有棋子的狀態(tài)信息。在禁手模式下,黑棋(玩家)落子時會檢查是否形成三三、四四、長連。如果是,該位置提示不可落棋。黑棋(AI)落子時,首先將所有可形成三三、四四、長連的禁手位置集合標記。通過博弈算法實現(xiàn)搜索時,對于不屬于禁手位置集合的點,采用估值函數(shù)給每個點打分,得到最優(yōu)位置。在無禁手模式下,只要落棋位置空,即可落子。
第一種搜索算法:極大極小搜索算法。其偽代碼如下所示:
極大極小算法偽代碼(參考)
01:int MaxMin(position,int d)
02:{
03: int bestvalue,value;
04: if(game over) //檢查游戲是否結(jié)束
05: return evaluation(p);// 游戲結(jié)束
06: if(depth<=0) //檢查是否是葉子節(jié)點
07: return evaluation(p);//返回估值
08: if(max) //極大值點
09: bestvalue=-INFINTY;
10: else //極小值點
11: bestvalue=INFINTY;
12: for(each possibly move m)
13: {
14: MakeMove(m); //走棋
15: value=MaxMin(p,d-1);
16: UnMakeMove(m); //恢復(fù)當(dāng)前局面
17: if(max)
18: bestvalue=max(value,bestvalue);//取最大值
19: else
20: bestvalue=min(value,bestvalue);//取最小值
21: }
22: return bestvalue;
23:}
另一種搜索算法:Alpha-beta剪枝算法。其為代碼如下所示:
Alpha-beta剪枝算法偽代碼(參考)
01: int AlphaBeta(nPlay,nAlpha,nBeta)
02: {
03: if(game over)
04: return Eveluation; //勝負返回估值
05: if(nPly==0)
06: return Eveluation; //葉節(jié)點返回估值
07: if(Is Min Node) //判斷節(jié)點類型
08: { // 極小值節(jié)點
09: for(each possible move m)
10: {
11: make move m; //生成新節(jié)點
12: score=AlphaBeta(nPly-1,nAlpha,nBeta)
13: unmake move m;//撤銷搜索過的節(jié)點
14: if(score 15: { 16: nBeta=score;//取極小值 17: if(nAlpha>=nBeta) 18: return nAlpha;//alpha剪枝 19: } 20: } 21: return nBeta;//返回最小值 22: } 23: else 24: {//取極大值的節(jié)點 25: for(each possible move m) 26: { 27: make move m; //生成新節(jié)點 28: //遞歸搜索子節(jié)點 29: score=AlphaBeta(nPly-1,nAlpha,nBeta) 30: //撤銷搜索過的節(jié)點 31: unmake move m; 32: if(score>nAlpha) 33: { 34: nAlpha=score;//取極小值 35: if(nAlpha>=nBeta) 36: //Beta剪枝 37: return nBeta; 38: } 39: } 40: return nAlpha;//返回最小值 41: } 42:} (4)游戲勝負的判定。 當(dāng)有一方獲勝時,最后一顆棋子的落棋處必然存在5子相連的棋型。其中連線的方式有:橫、豎、左對角線、右對角線四種(如圖4所示)。在實現(xiàn)階段,需要在各棋子落下時,以該棋子為中心位置判斷它的四種方向上是否存在五子相連。 圖4 五子相連的四種情況 (5)背景音樂的添加。 通過using System.Media語句引入用于播放聲音文件類的命名空間,然后通過聲明SoundPlayer類的對象來實現(xiàn)音樂播放的功能。 音樂播放偽代碼(參考) 01: SoundPlayer bgm = new SoundPlayer(音樂文件路徑); 02: bgm.Play();//開始播放音樂 03: bgm.Stop();//停止音樂播放 軟件擴展功能較多,其他功能不在此一一列舉。 本案例建立面向全過程的多元化課程考核形式,將體現(xiàn)自主探究過程的課堂和課后表現(xiàn)、項目實踐等納入實驗綜合考評體系,合理設(shè)置權(quán)重系數(shù)。成績采用百分制,占比如下: (1)平時成績(10%);是否按時參加實驗課,是否積極參加小組討論。 (2)基礎(chǔ)功能完成(40%):基本功能是否運行正確,代碼書寫是否規(guī)范?;A(chǔ)功能內(nèi)容如表2所示。 (3)提高功能完成(30%):提高功能是否運行正確,代碼書寫是否規(guī)范。提高功能內(nèi)容如表2所示。 表2 功能實現(xiàn)的兩個層次 (4)項目答辯(10%):答辯時表述問題需要有一定的條理性和邏輯性,回答問題需要正確和準確。 (5)實驗報告(10%):以論文形式書寫報告,掌握科學(xué)論文的書寫格式、書寫方法及參考文獻引用等,需要體現(xiàn)其規(guī)范性和完整性,實驗報告需要有以下內(nèi)容: ①問題描述。描述本案例實驗背景、需要解決的問題,涉及的知識點,問題規(guī)模,難度、調(diào)研情況。 ②系統(tǒng)設(shè)計。重點闡述實驗的設(shè)計總體思路,包括功能設(shè)計和界面設(shè)計。 ③實現(xiàn)方法。運用了哪些核心技術(shù),引用所查閱的資料,如教材、論文、網(wǎng)站等。 ④實現(xiàn)過程。詳細描述實驗過程,重點記錄關(guān)鍵步驟并解釋說明,粘貼相關(guān)代碼。 ⑤實驗結(jié)果。設(shè)計測試用例,程序運行結(jié)果截圖。 ⑥實驗總結(jié)??偨Y(jié)在實驗中學(xué)習(xí)和掌握了哪些知識,個人哪些方面能力有提高,案例還有哪些改進的地方。 本案例已納入本?!禖#程序設(shè)計》課程的實驗教學(xué)中,面向全校的數(shù)學(xué)、化學(xué)、生物、信息專業(yè)的大二學(xué)生開設(shè)。平均每年有近10個教學(xué)班400余名學(xué)生完成本案例的實踐。經(jīng)過近兩年的教學(xué)案例的實施,學(xué)生程序設(shè)計能力得到明顯的提升。學(xué)生作品如圖5所示。 圖5 學(xué)生作品 本案例通過實現(xiàn)一個趣味性很強的游戲,引導(dǎo)學(xué)生思考如何將所學(xué)知識和現(xiàn)實應(yīng)用聯(lián)系起來。案例內(nèi)容涉及到C#程序設(shè)計課程中的核心知識點,通過構(gòu)思、設(shè)計、實現(xiàn)和運作過程,循序漸進地將相關(guān)知識點融入案例的任務(wù)中,幫助同學(xué)建立知識的前后聯(lián)系。使同學(xué)體會到知識的系統(tǒng)性和應(yīng)用性。 根據(jù)學(xué)生不同的專業(yè)特點,設(shè)計了案例中有不同的側(cè)重點,并從不同層次的功能實現(xiàn)入手,提高學(xué)生的動手實踐和自主研學(xué)能力,最后使之形成終身學(xué)習(xí)的習(xí)慣。 該案例目前在實驗教學(xué)中進行了實際應(yīng)用,學(xué)生反饋編程能力增強了,學(xué)習(xí)效果良好。3.4 實驗案例的考核與驗收
3.5 實驗案例的應(yīng)用
4 結(jié)束語