中级实训总结报告
目錄【閱讀時(shí)間:約30分鐘】
- 中級實(shí)訓(xùn)總結(jié)報(bào)告
- 姓名:隱藏敏感信息 學(xué)號:隱藏敏感信息
- 一、階段1:項(xiàng)目啟動
- 1、Vi/Vim
- 2、Java
- 3、Ant
- 4、Junit
- 5、SonarQube
- 6、 編譯運(yùn)行BugRunner
- 二、階段2:基本任務(wù)
- 1. part2的工作
- (1)CircleBug
- (2)SpiralBug
- (3)ZBug
- (4)DancingBug
- 2. part3的工作
- 1. Test for `act()`
- 2. Test for `jump()`
- 3. Test for `canJump()`
- 4. Result
- 3. part4的工作
- (1)ModifiedChameleonCritter
- (2)ChameleonKid
- (3)RockHound
- (4)BlusterCritter
- (5)QuickCrab
- (6)KingCrab
- 4. part5的工作
- (1)SparseGridNode
- (2)SparseBoundedGrid
- (3)SparseBoundedGrid2
- (4)UnboundedGrid2
- (5)SparseGridRunner
- 三、階段3:擴(kuò)展任務(wù)
- 1. imagereader的工作
- 2. imagereader的readme文件
- (1)ImagaReaderRunner.java
- (2)ImplementImageIO.java
- (3)ImplementImageProcessor.java
- (4)ImageProcessorTest.java
- 3. mazebug的工作
- 4. mazebug的readme文件
- (1)MazeBugRunner.java
- (2)MazeBug.java
- (3)Examples
- 5. jigsaw的工作
- 四、總結(jié)和心得體會
中級實(shí)訓(xùn)總結(jié)報(bào)告
姓名:隱藏敏感信息 學(xué)號:隱藏敏感信息
持續(xù)了五周的中級實(shí)訓(xùn)終于快結(jié)束啦,今天筆者來總結(jié)一下整個中級實(shí)訓(xùn)過程中的付出與收獲。不妨回顧了一下中級實(shí)訓(xùn)的架構(gòu),可以說中級實(shí)訓(xùn)安排還算比較合理,從Vim+Java+Ant+Junit等工具的使用,到初步了解girdworld的架構(gòu)和設(shè)計(jì)思想,再到自己手動完成girdworld的一部分代碼,這一段時(shí)間是一個循序漸進(jìn)的時(shí)期,對于不太熟悉整個軟件開發(fā)流程的筆者而言,收獲是非常大的。最后還有一個擴(kuò)展任務(wù),個人認(rèn)為是基本任務(wù)的兩倍難度,不過得益于網(wǎng)絡(luò)資料和TA們的指導(dǎo),筆者還是順利地完成了三個擴(kuò)展任務(wù),可謂是很滿足了~
此外,在檢查階段TA們也很盡心盡責(zé),會詢問各個階段的代碼以及其實(shí)現(xiàn)思想,也會指出代碼存在哪些問題,再次感謝~
一、階段1:項(xiàng)目啟動
在階段1,我們主要是圍繞Vim+Java+Ant+Junit展開,具體流程可概括為:使用Vim來編寫Java代碼, 利用Ant實(shí)現(xiàn)Java代碼的自動編譯,利用Junit來進(jìn)行單元測試。由于在階段1并沒有進(jìn)行過多與gridworld相關(guān)的代碼實(shí)現(xiàn),此處就簡述一下各個部分的學(xué)習(xí)心得吧。
1、Vi/Vim
Vim編譯器是一款久負(fù)盛名的編譯器,常常被稱為“編譯器之神”或“大神用的編譯器”,它是linux中最經(jīng)典的文本編輯器,支持代碼補(bǔ)全、編譯及錯誤跳轉(zhuǎn)等功能。對于雖然不能說完全掌握Vim但也有幾年使用經(jīng)驗(yàn)的筆者來說,筆者更傾向于在CLI界面下使用Vim,比如實(shí)驗(yàn)室服務(wù)器往往就通過ssh連接linux terminal,這時(shí)使用Vim編輯器可以很好地編寫各種代碼。而在GUI界面下,筆者更傾向于使用sublime或eclipse來編寫java代碼,特別是后者在設(shè)置了熟悉的自動補(bǔ)全后,可以提升地提升工作效率。當(dāng)然,在此次實(shí)訓(xùn)中筆者會盡量使用Vim來熟悉項(xiàng)目開發(fā)流程。
Vim編譯器一共有命令模式、末行模式和編輯模式這三種工作模式,這三種模式的區(qū)別可以用以下一段話來概括。使用Vim打開文件時(shí)首先進(jìn)入的就是命令模式,可以看做Vim編譯器的入口,可以進(jìn)行復(fù)制粘貼等操作;在命令模式按 i 便會進(jìn)入編輯模式,可以進(jìn)行各種代碼或文字編輯操作,再按esc便會退回到命令模式;在命令模式按輸入 : 進(jìn)入末行模式,可以進(jìn)行保存退出等操作。
筆者常用的Vim指令有以下這些:
? ①打開文件:在terminal輸入vim filename
? ②保存并退出:在末行模式輸入wq
? ③復(fù)制一行代碼:在命令行模式輸入yy
? ④粘貼:在命令行模式輸入p
? ⑤刪除一行代碼:在命令行模式輸入dd
? ⑥撤銷操作:在命令行模式輸入u
? ⑦在編輯模式下:與文本編輯類似,方向鍵移動,正常鍵盤輸入
2、Java
由于在大二時(shí)筆者已選修了Java語言開發(fā)的相關(guān)課程,因此在進(jìn)行這部分的自學(xué)時(shí)比較輕松,主要是溫故了Java的基本語法和使用命令行編譯運(yùn)行Java程序。
在筆者看來Java是是一門典型的面向?qū)ο缶幊陶Z言,它盡可能地進(jìn)行封裝,擁有豐富的庫函數(shù)與接口支持,對于協(xié)同開發(fā)效率很高,這也是Java常年位于編程語言排行榜前列的原因之一。Java程序的編寫流程也不復(fù)雜,以熟悉的eclipse工具為例,主要流程是創(chuàng)建一個Java project,創(chuàng)建相應(yīng)的package和class,然后寫main函數(shù)與要調(diào)用的函數(shù)即可。此處便省略基礎(chǔ)的Java語法介紹了,直接用一個簡單的helloworld例子來說明如何使用命令行創(chuàng)建、編譯并運(yùn)行Java程序吧~~
首先創(chuàng)建并書寫代碼:
然后使用命令行編譯運(yùn)行Java程序:
此外,雖然云桌面的環(huán)境變量已經(jīng)配置了很多,但是直接執(zhí)行java helloworld還是會報(bào)錯: could not find or load main class helloworld 。
經(jīng)搜索相關(guān)資料,是java的環(huán)境設(shè)置不完全,只要在 ~/ .bashrc和~/ .profile添加以下的環(huán)境變量設(shè)置即可:
進(jìn)入 ~/.bashrc和~/.profile的指令如下:
gedit ~/.bashrc
gedit ~/.profile要添加的環(huán)境變量如下:
export JAVA_HOME=~/Desktop/java1.8
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
3、Ant
正如上文所述,Ant可用于實(shí)現(xiàn)Java代碼的自動編譯,Ant是一個將軟件編譯、測試、部署等步驟聯(lián)系在一起加以自動化的一個工具,大多用于Java環(huán)境中的軟件開發(fā)。根據(jù)筆者的自學(xué)體會,它的作用與C/C++的makefile類似,一次性編譯多個文件,大大方便了項(xiàng)目編譯的流程,能減少很多重復(fù)的操作。
Ant使用xml格式來編寫,若要在項(xiàng)目中使用Ant,首先要在項(xiàng)目文件下面創(chuàng)建build.xml文件,并在xml文件中編寫與項(xiàng)目相關(guān)的指令。編寫完畢后,在項(xiàng)目文件下執(zhí)行ant指令即可。
Ant中主要的元素有三種,分別是、和。
(1)project 元素是 Ant 構(gòu)件文件的根元素, Ant 構(gòu)件文件至少應(yīng)該包含一個 project 元素,否則會發(fā)生錯誤。在每個 project 元素下,可包含多個 target 元素。 project 元素有如下屬性:
? ①name 屬性:用于指定 project 元素的名稱
? ②default 屬性:用于指定 project 默認(rèn)執(zhí)行時(shí)所執(zhí)行的 target 的名稱
? ③basedir 屬性:用于指定基路徑的位置。該屬性沒有指定時(shí),使用 Ant 的構(gòu)件文件的附目錄
作為基準(zhǔn)目錄。
(2)target 元素是Ant 的基本執(zhí)行單元,它可以包含一個或多個具體的任務(wù)。多個 target 可以存在相互依賴關(guān)系。target 元素有如下屬性:
? ①name 屬性:指定 target 元素的名稱,這個屬性在一個 project 元素中是唯一的。我們可以通過指定 target 元素的名稱來指定某個 target
? ②depends 屬性:用于描述 target 之間的依賴關(guān)系,若與多個 target 存在依賴關(guān)系時(shí),需
要以“,”間隔。 Ant 會依照 depends 屬性中 target 出現(xiàn)的順序依次執(zhí)行每個target。被依賴的 target 會先執(zhí)行
? ③if 屬性:用于驗(yàn)證指定的屬性是否存在,若不存在,所在 target 將不會被執(zhí)行
? ④unless 屬性:該屬性的功能與 if 屬性的功能正好相反,它也用于驗(yàn)證指定的屬性是否存在,若不存在,所在 target 將會被執(zhí)行
? ⑤description 屬性:該屬性是關(guān)于 target 功能的簡短描述和說明
(3)property 元素:該元素可看作參量或者參數(shù)的定義,類似于C/C++中的宏定義,可以用一些我們便于理解的變量名來代替復(fù)雜的路徑,提高代碼的可讀性。
project 的屬性可以通過 property元素來設(shè)定,也可在 Ant 之外設(shè)定。若要在外部引入某文件,例如 build.properties 文件,可以通過如下內(nèi)容將其引入: <property file=” build.properties”/>
下面不妨借助【二、Java】中的helloworld代碼,編寫一個簡單的build.xml來體驗(yàn)Ant工具。在項(xiàng)目目錄helloworld-ant中,將helloworld.java放在src文件夾中,在項(xiàng)目目錄下創(chuàng)建build.xml內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?><!-- Created by 18342026 --><project name="helloworld" default="run" basedir="."><property name="src_path" value="src"/><property name="class_path" value="classes"/><target name="clean"><delete dir="${class_path}"/></target><target name="compile" depends="clean"><mkdir dir="${class_path}"/><javac srcdir="${src_path}" destdir="${class_path}"/></target><target name="run" depends="compile"><java classname="helloworld" classpath="${class_path}" /></target></project>
在項(xiàng)目目錄下運(yùn)行指令ant,即可自動編譯并運(yùn)行如下:
4、Junit
Junit是Java的一種單元測試框架,它所做的測試是白盒測試,常用于測試驅(qū)動開發(fā)(Test-Driven Development,簡稱TDD) 。由于之前選修的Java課程已接觸過Junit框架,并且最近也通過Golang課程學(xué)習(xí)了TDD開發(fā)流程的思維,其實(shí)就是通過函數(shù)的輸入輸出來確定函數(shù)的正確性。
要使用Junit,需要配置好環(huán)境,并將junit的jar文件放到項(xiàng)目目錄中。由于筆者查看的junit教程版本為junit 4.10,仍然以簡單的helloworld程序?yàn)槔?#xff0c;在項(xiàng)目目錄helloworld-junit中,放入junit-4.10.jar、編寫helloworld.java和Test_helloworld.java文件。
其中helloworld.java代碼為:
public class helloworld{public static void main(String[] argv) {System.out.println(sayhello());} public static String sayhello() {return "Hello World By 18342026!";}
}
Test_helloworld.java代碼為:
import static org.junit.Assert.*;
import org.junit.Test;public class Test_helloworld{@Test public void test_helloworld() {String res = helloworld.sayhello();assertEquals("Hello World By 18342026!", res);}
}
最后執(zhí)行以下指令即可運(yùn)行junit進(jìn)行單元測試:
javac -classpath .:junit-4.10.jar Test_helloworld.java
java -classpath .:junit-4.10.jar -ea org.junit.runner.JUnitCore Test_helloworld
運(yùn)行截圖:
5、SonarQube
由于中級實(shí)訓(xùn)階段一要用到SonarQube,雖然沒有要求寫相應(yīng)的報(bào)告,但筆者感覺書寫報(bào)告還是對提高軟件的熟練度有幫助的。
Sonar 是一個用于代碼質(zhì)量管理的開源平臺,用于管理源代碼的質(zhì)量,可以從七個維度檢測代碼質(zhì)量。通過插件形式,可以支持包括 java,C#,C/C++,PL/SQL,Cobol,JavaScrip,Groovy 等等二十幾種編程語言的代碼質(zhì)量管理與檢測。
經(jīng)檢查,云桌面已配置好Sonar了:
在 shell 里面鍵入 cd $SONAR_HOME, 可以直接進(jìn)入啟動目錄。 在 shell 里面鍵入
./sonar.sh start 啟動服務(wù)
./sonar.sh stop 停止服務(wù)
./sonar.sh restart 重啟服務(wù)
訪問 http:\localhost:9000, 如果顯示 Sona輸入rQube 的項(xiàng)目管理界面,表示安裝成功:
以階段一的calculator程序?yàn)槔?#xff0c;在項(xiàng)目目錄下創(chuàng)建sonar-project.properties,其中只需要將sonar.projectKey、sonar.projectName和java-module.sonar.projectBaseDir的值修改為源碼所在的文件夾即可,具體內(nèi)容如下:
然后在當(dāng)前目錄下輸入sonar-runner,再進(jìn)入http:\localhost:9000查看程序的代碼分析即可:
經(jīng)過Major的提示修改后,學(xué)到了不少代碼風(fēng)格的知識,修改后如下:
6、 編譯運(yùn)行BugRunner
在倉庫( https://github.com/se-2020/se-2020.github.io/tree/master/resources )中下載gridworld.zip并解壓,再將里面的GridWorldCode.zip解壓出來,進(jìn)入目錄GridWorldCode\projects\firstProject\中,即可看到BugRunner.java代碼。
由于環(huán)境在之前已配置完畢,直接在當(dāng)前目錄下運(yùn)行以下指令,即可進(jìn)入BugRunner程序中:
javac -classpath .:./../../gridworld.jar BugRunner.java
java -classpath .:./../../gridworld.jar BugRunner
再來簡單介紹一下BugRunner,首先用戶可以自行在格子中放置可移動的蟲子以及不可移動的花和石頭,然后點(diǎn)擊step是移動一步,點(diǎn)擊run是自動開始移動多步,slow/fast的游標(biāo)可以調(diào)節(jié)移動速度,stop則是停止。其他更詳細(xì)的信息可看Part1的回答。
二、階段2:基本任務(wù)
在階段2,我們正式進(jìn)入了gridworld的代碼世界,從part2到part5,一共需要完成14個子模塊的代碼,代碼量還是比較大的。不過由于在實(shí)訓(xùn)博客都給出了詳細(xì)的設(shè)計(jì)介紹,并且還有相應(yīng)的示例,所以往往在寫完一個子模塊的代碼后,就能遷移至其他子模塊,編寫代碼的速度也快了不少。此外,筆者還學(xué)習(xí)了如何寫規(guī)范的readme文件便于其他人快速了解程序的使用,雖然階段2的代碼簡單,但文檔和sonnar-qube的修改等,還是花了筆者三四天的時(shí)間。
下面還是按照part2到part5來介紹一下這個階段所做的工作(因?yàn)閷懥擞⑽牡膔eadme,此處就直接引用啦):
1. part2的工作
This project can be run using Eclipse. Its introduction and running screenshots are listed below. We can also use Sonar-Qube for code analysis of project files.
(1)CircleBug
Write a class CircleBug that is identical to BoxBug, except that in the act method the turn method is called once instead of twice.
(2)SpiralBug
Write a class SpiralBug that drops flowers in a spiral pattern. Hint: Imitate BoxBug, but adjust the side length when the bug turns.
?
(3)ZBug
Write a class ZBug to implement bugs that move in a “Z” pattern, starting in the top left corner. After completing one “Z” pattern, a ZBug should stop moving. In any step, if a ZBug can’t move and is still attempting to complete its “Z” pattern, the ZBug does not move and should not turn to start a new side. Supply the length of the “Z” as a parameter in the constructor. The following image shows a “Z” pattern of length 4. Hint: Notice that a ZBug needs to be facing east before beginning its “Z” pattern.
(4)DancingBug
Write a class DancingBug that “dances” by making different turns before each move. The DancingBug constructor has an integer array as parameter. The integer entries in the array represent how many times the bug turns before it moves. For example, an array entry of 5 represents a turn of 225 degrees (recall one turn is 45 degrees). When a dancing bug acts, it should turn the number of times given by the current array entry, then act like a Bug. In the next move, it should use the next entry in the array. After carrying out the last turn in the array, it should start again with the initial array value so that the dancing bug continually repeats the same turning pattern. The DancingBugRunner class should create an array and pass it as aparameter to the DancingBug constructor.
2. part3的工作
這部分我們要涉及一個jumper對象,其設(shè)計(jì)如下:
#### 1. What will a jumper do if the location in front of it is empty, but the location two cells in front contains a flower or a rock?**Answer:**①If the location contains a flower, it will jump onto the flower without leaving a flower in the previous location.②If the location contains a rock, it will turn 45 degrees to the right.#### 2. What will a jumper do if the location two cells in front of the jumper is out of the grid?**Answer:**①If called by `jump()` method, it will remove the jumper as if it jumps out of the grid.②If called by `act()` methods, it will turn 45 degrees to the right.#### 3. What will a jumper do if it is facing an edge of the grid?**Answer:**①If called by `jump()` method, remove the jumper as if it jumps out of the grid.②If called by `act()` methods, turn 45 degrees to the right.#### 4. What will a jumper do if another actor (not a flower or a rock) is in the cell that is two cells in front of the jumper?**Answer:**It will turn 45 degrees to the right.#### 5. What will a jumper do if it encounters another jumper in its path?**Answer:**①If the location one cells in front of the jumper is another jumper, it will jump.②If the location two cells in front of the jumper is another jumper, it will turn 45 degrees to the right.#### 6. Are there any other tests the jumper needs to make?**Answer:**When the jumper jump onto a flower, then it leaves, the flower will disappear.When the location in front of the jumper contains a bug, it can’t jump onto the bug.
此外,我們還要編寫單元測試來檢測設(shè)計(jì)的正確性,此處筆者根據(jù)jumper的特性設(shè)計(jì)了19個單元測試(主要分為act()、jump()和 canJump()函數(shù)的測試):
For testing for Jumper, the main methods to be tested are act(), jump() and canJump(). According to the design report, I designed 9 tests for act(), 2 tests for jump(), and 8 tests for canJump().
1. Test for act()
(1)test jump over when the location one cells in front contains a rock
@Testpublic void testAct0() {//test jump over when the location one cells in front contains a rockworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(4, 5), new Rock());world.step();assertEquals(new Location(3, 5), jumper.getLocation());}
(2)test jump over when the location one cells in front contains a flower
@Testpublic void testAct1() {//test jump over when the location one cells in front contains a flowerworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(4, 5), new Flower());world.step();assertEquals(new Location(3, 5), jumper.getLocation());}
(3)test turn when the location two cells in front contains a rock
@Testpublic void testAct2() {//test turn when the location two cells in front contains a rockworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Rock());world.step();assertEquals(45, jumper.getDirection());}
(4)test jump when the location two cells in front contains a flower
@Testpublic void testAct3() {//test jump when the location two cells in front contains a flowerworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Flower());world.step();assertEquals(new Location(3, 5), jumper.getLocation());}
(5)test turn when the location two cells in front of the jumper is out of the grid
@Testpublic void testAct4() {//test turn when the location two cells in front of the jumper is out of the gridworld = new ActorWorld();jumper = new Jumper();world.add(new Location(1, 5), jumper);world.step();assertEquals(45, jumper.getDirection());}
(6)test turn when the the jumper is facing an edge of the grid
@Testpublic void testAct5() { //test turn when the the jumper is facing an edge of the gridworld = new ActorWorld();jumper = new Jumper();world.add(new Location(0, 5), jumper);world.step();assertEquals(45, jumper.getDirection());}
(7)test turn when another actor (not a flower or a rock) is in the cell that is two cells in front of the jumper
@Testpublic void testAct6() {//test turn when another actor (not a flower or a rock) is in the cell that is two cells in front of the jumperworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Actor());world.step();assertEquals(45, jumper.getDirection());}
(8)test turn when jumper encounters another jumper in its path
@Testpublic void testAct7() {//test turn when jumper encounters another jumper in its pathworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);Jumper jumper2 = new Jumper();world.add(new Location(3, 5), jumper2);jumper2.setDirection(180);world.step();assertEquals(45, jumper.getDirection());assertEquals(225, jumper2.getDirection());}
(9)test no left flower when jumer leaves on a flower
@Testpublic void testAct8() {//test no left flower when jumer leaves on a flowerworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Flower());world.step();assertEquals(new Location(3, 5), jumper.getLocation());world.step();assertEquals(null, jumper.getGrid().get(new Location(3, 5)));}
2. Test for jump()
(1)test remove when jumper is facing an edge of the grid
@Testpublic void testJump0() {//test remove when jumper is facing an edge of the gridworld = new ActorWorld();jumper = new Jumper();world.add(new Location(0, 5), jumper);jumper.jump();assertEquals(null, jumper.getGrid());}
(2)test remove when the location two cells in front of the jumper is out of the grid
@Testpublic void testJump1() {//test remove when the location two cells in front of the jumper is out of the gridworld = new ActorWorld();jumper = new Jumper();world.add(new Location(1, 5), jumper);jumper.jump();assertEquals(null, jumper.getGrid());}
3. Test for canJump()
(1)test true when jump over when the location one cells in front contains a rock
@Testpublic void testCanJump0() {//test true when jump over when the location one cells in front contains a rockworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(4, 5), new Rock());assertEquals(true, jumper.canJump());}
(2)test true when jump over when the location one cells in front contains a flower
@Testpublic void testCanJump1() {//test true when jump over when the location one cells in front contains a flowerworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(4, 5), new Flower());assertEquals(true, jumper.canJump());}
(3)test false when the location two cells in front contains a rock
@Testpublic void testCanJump2() {//test false when the location two cells in front contains a rockworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Rock());assertEquals(false, jumper.canJump());}
(4)test true when the location two cells in front contains a flower
@Testpublic void testCanJump3() {//test true when the location two cells in front contains a flowerworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Flower());assertEquals(true, jumper.canJump());}
(5)test false when the location two cells in front of the jumper is out of the grid
@Testpublic void testCanJump4() {//test false when the location two cells in front of the jumper is out of the gridworld = new ActorWorld();jumper = new Jumper();world.add(new Location(1, 5), jumper);assertEquals(false, jumper.canJump());}
(6)test false when the the jumper is facing an edge of the grid
@Testpublic void testCanJump5() {//test false when the the jumper is facing an edge of the gridworld = new ActorWorld();jumper = new Jumper();world.add(new Location(0, 5), jumper);assertEquals(false, jumper.canJump());}
(7)test false when another actor (not a flower or a rock) is in the cell that is two cells in front of the jumper
@Testpublic void testCanJump6() {//test false when another actor (not a flower or a rock) is in the cell that is two cells in front of the jumperworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);world.add(new Location(3, 5), new Actor());assertEquals(false, jumper.canJump());}
(8)test false when encounters another jumper in its path
@Testpublic void testCanJump7() {//test false when encounters another jumper in its pathworld = new ActorWorld();jumper = new Jumper();world.add(new Location(5, 5), jumper);Jumper jumper2 = new Jumper();world.add(new Location(3, 5), jumper2);jumper2.setDirection(180);assertEquals(false, jumper.canJump());assertEquals(false, jumper2.canJump());}
4. Result
3. part4的工作
This project can be run using Eclipse. Its introduction and running screenshots are listed below. We can also use Sonar-Qube for code analysis of project files.
(1)ModifiedChameleonCritter
Modify the processActors method in ChameleonCritter so that if the list of actors to process is empty, the color of the ChameleonCritter will darken (like a flower).
(2)ChameleonKid
Create a class called ChameleonKid that extends ChameleonCritter as modified in exercise 1. A ChameleonKid changes its color to the color of one of the actors immediately in front or behind. If there is no actor in either of these locations, then the ChameleonKid darkens like the modified ChameleonCritter.
(3)RockHound
Create a class called RockHound that extends Critter. A RockHound gets the actors to be processed in the same way as a Critter. It removes any rocks in that list from the grid. A RockHound moves like a Critter.
?
(4)BlusterCritter
Create a class BlusterCritter that extends Critter. A BlusterCritter looks at all of the neighbors within two steps of its current location. (For a BlusterCritter not near an edge, this includes 24 locations). It counts the number of critters in those locations. If there are fewer than c critters, the BlusterCritter’s color gets brighter (color values increase). If there are c or more critters, the BlusterCritter’s color darkens (color values decrease). Here, c is a value that indicates the courage of the critter. It should be set in the constructor.
(5)QuickCrab
Create a class QuickCrab that extends CrabCritter. A QuickCrab processes actors the same way a CrabCritter does. A QuickCrab moves to one of the two locations, randomly selected, that are two spaces to its right or left, if that location and the intervening location are both empty. Otherwise, a QuickCrab moves like a CrabCritter.
(6)KingCrab
Create a class KingCrab that extends CrabCritter. A KingCrab gets the actors to be processed in the same way a CrabCritter does. A KingCrab causes each actor that it processes to move one location further away from the KingCrab. If the actor cannot move away, the KingCrab removes it from the grid. When the KingCrab has completed processing the actors, it moves like a CrabCritter.
4. part5的工作
This project can be run using Eclipse. Its introduction and running screenshots are listed below. We can also use Sonar-Qube for code analysis of project files.
(1)SparseGridNode
A SparseGridNode is a basic node in the grid, namely a small lattice.
(2)SparseBoundedGrid
A SparseBoundedGrid is a rectangular grid with bounded edges and sparse objects in the gird implemented by sparse array.
(3)SparseBoundedGrid2
A SparseBoundedGrid2 is a rectangular grid with bounded edges and sparse objects in the gird implemented by HashMap.
(4)UnboundedGrid2
An UnboundedGrid is a rectangular grid with unbounded number of rows and columns. When the access exceeds the current size, its size will be doubled.
(5)SparseGridRunner
This class runs a world with additional grid choices. We can use the SparseBoundedGrid, SparseBoundedGrid2 or UnboundedGrid2 methods to build a grid world.
三、階段3:擴(kuò)展任務(wù)
階段3的擴(kuò)展任務(wù)分為imagereader、mazebug和jigsaw,這三者毫無疑問是整個中級實(shí)訓(xùn)最難的一部分,其中主要的難點(diǎn)在于理解任務(wù)與框架,java的編程實(shí)現(xiàn)也比階段2按模板“填鴨式”的編程要難上許多。
但好在各個part都至少有一個現(xiàn)成的示例和詳細(xì)的設(shè)計(jì)框架,比如mazebug給出了編程的全過程和灰度圖的算法,只差實(shí)現(xiàn)了;mazebug給出了深度優(yōu)先搜索的思路,然后自己在這基礎(chǔ)上再完成一個方向預(yù)測的小設(shè)計(jì)即可順利完成;jigsaw給出了啟發(fā)式搜索算法的代碼,我們只需要借鑒實(shí)現(xiàn)廣度優(yōu)先搜索和設(shè)計(jì)啟發(fā)式搜索算法的cost函數(shù)即可。
由于階段3也完善了readme文件,此處筆者簡單總結(jié)各個任務(wù)的核心算法,并結(jié)合readme文件進(jìn)行介紹如下:
1. imagereader的工作
在實(shí)訓(xùn)的博客中,介紹了位圖文件的存儲結(jié)構(gòu),為了提取主要的像素色彩信息,我們需要從位圖中將它們分離出來:
class ImplementImageIO implements IImageIO{/** 利用二進(jìn)制流讀取Bitmap位圖文件* 注意,這里要求不能使用Java提供的API直接讀取圖像* IImageIO必須實(shí)現(xiàn)myRead函數(shù)
*/public Image myRead(String filepath) throws IOException {try {FileInputStream imageStream = new FileInputStream(filepath);
/** 丟棄無用信息* #18-21 保存位圖寬度(以像素個數(shù)表示)* #22-25 保存位圖高度(以像素個數(shù)表示)*/imageStream.skip(18);byte[] widthByte = new byte[4];imageStream.read(widthByte);byte[] heightByte = new byte[4];imageStream.read(heightByte);int width = byte2Int(widthByte);int height = byte2Int(heightByte);int[] data = new int[width * height];/*
* 丟棄無用信息
* #54-x 位圖數(shù)據(jù)
*/imageStream.skip(28);int skipNum = (4 - width * 3 % 4) % 4;for(int i = height - 1; i >= 0; --i) {for(int j = 0; j < width; ++j) {int blue = imageStream.read() & 0xff;int green = imageStream.read() & 0xff;int red = imageStream.read() & 0xff;Color color = new Color(red, green, blue);data[i * width + j] = color.getRGB();}imageStream.skip(skipNum);}imageStream.close();return Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(width, height, data, 0, width));}catch (Exception e) {e.printStackTrace();return null;}//return null;}public int byte2Int(byte[] tmp) {int t1 = tmp[3] & 0xff;int t2 = tmp[2] & 0xff;int t3 = tmp[1] & 0xff;int t4 = tmp[0] & 0xff;int num = t1 << 24 | t2 << 16 | t3 << 8 |t4;return num;}/** 根據(jù)二進(jìn)制數(shù)據(jù)創(chuàng)建Image時(shí)可以使用API;* 把讀取彩色圖像轉(zhuǎn)換成灰度圖像;* 提取并且顯示彩色圖像各個色彩通道;* 把處理完的圖像保存為bmp格式圖像。* IImageIO必須實(shí)現(xiàn)myWrite函數(shù)
*/public Image myWrite(Image img, String filepath) throws IOException {try {File bmpFile = new File(filepath + ".bmp");BufferedImage bufferedImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);Graphics2D graph = bufferedImage.createGraphics();graph.drawImage(img, 0, 0, null);graph.dispose();ImageIO.write(bufferedImage, "bmp", bmpFile);return bufferedImage;} catch (Exception e) {e.printStackTrace();}return null;}}
此外,實(shí)訓(xùn)博客也有一個比較有趣的任務(wù),就是輸入色彩圖,轉(zhuǎn)換為紅藍(lán)綠灰圖,由于博客中已給出了紅藍(lán)綠圖和灰度圖的公式(見下面代碼的注釋),其核心算法即圖像處理算法的實(shí)現(xiàn)也比較簡單:
class ImplementImageProcessor implements IImageProcessor {public Image showGray(Image image) {try { return colorFilter(image, "gray");} catch (Exception e) {e.printStackTrace();}return null;}public Image showChanelB(Image image) {try { return colorFilter(image, "blue");} catch (Exception e) {e.printStackTrace();}return null;}public Image showChanelG(Image image) {try { return colorFilter(image, "green");} catch (Exception e) {e.printStackTrace();}return null;}public Image showChanelR(Image image) {try { return colorFilter(image, "red");} catch (Exception e) {e.printStackTrace();}return null;}private Image colorFilter(Image img, String color) {BufferedImage newImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);Graphics2D graph = newImage.createGraphics();graph.drawImage(img, 0, 0, null);graph.dispose();int width = img.getWidth(null);int height = img.getHeight(null);for(int i = 0; i < width; ++i) {for(int j = 0; j < height; ++j) {int rgb = newImage.getRGB(i, j);if(color == "red") {rgb = 0xffff0000 & rgb;}else if(color == "green") {rgb = 0xff00ff00 & rgb;}else if(color == "blue") {rgb = 0xff0000ff & rgb;}else {
/** 彩色圖像轉(zhuǎn)換成灰度圖像*將彩色圖轉(zhuǎn)換成灰度圖,建議采用NTSC推薦的彩色圖到灰度圖的轉(zhuǎn)換公式:*I = 0.299 * R + 0.587 * G + 0.114 *B,其中R,G,B分別為紅、綠、藍(lán)通道的顏色值。*然后將三個色彩通道的顏色值改為這個值即可。這樣,原來的彩色圖像就變成了灰度圖像了。*/int gray = (int)(((rgb & 0x00ff0000) >> 16) * 0.299 + ((rgb & 0x0000ff00) >> 8) * 0.587 + (rgb & 0x000000ff) * 0.114);rgb = (rgb & 0xff000000) + (gray << 16) + (gray << 8) + gray;}newImage.setRGB(i, j, rgb);}}return newImage;}}
2. imagereader的readme文件
This project can be run using Eclipse. Its introduction and running screenshots are listed below. We can also use Sonar-Qube for code analysis of project files.
The overall structure of the project is as follows:
(1)ImagaReaderRunner.java
This Java file is the entry to the program. Running this file calls the IImageIO and IImageProcessormethods.
(2)ImplementImageIO.java
The ImplementImageIO Implements the read and write operations on images in the IImageIO interface .
(3)ImplementImageProcessor.java
The ImplementImageProcessor implements the operation for image single-channel extraction in the IImageProcessor interface.
(4)ImageProcessorTest.java
After importing junit.jar, we run this file to test the correctness of the program’s four functions with two original images in the ImageReader\src\bmptest folder.
3. mazebug的工作
mazebug也是一個比較有趣的任務(wù),它基于階段2所實(shí)現(xiàn)的girdworld,要求實(shí)現(xiàn)跑迷宮的功能,這個功能在筆者完成階段2的基本任務(wù)時(shí)完全沒有想到,一定程度上也讓筆者意識到簡單的框架也可以做出很多有趣的東西啊~
mazebug的代碼實(shí)現(xiàn)并不難,只是使用了基本的深度優(yōu)先搜索算法,并在這基礎(chǔ)上添加了方向預(yù)測的特性,具體實(shí)現(xiàn)的代碼如下:
public class MazeBug extends Bug {
/** Location next 記錄下一步要行走到的位置* Location last 記錄上一步的位置,便于在走到死路盡頭時(shí)返回* Stack<ArrayList<Location>> crossLocation 記錄樹的節(jié)點(diǎn)的棧* Integer stepCount 記錄本迷宮走到出口所用的步數(shù)*/public Location next;public Location last;public boolean isEnd = false;public Stack<ArrayList<Location>> crossLocation = new Stack<ArrayList<Location>>();public Integer stepCount = 0;public boolean hasShown = false;/** boolean visit[][] 訪問矩陣* ArrayList<Location> branch 棧頂節(jié)點(diǎn)和已經(jīng)訪問的路徑節(jié)點(diǎn)* int []weight 四個方向的權(quán)值*/public boolean visit[][];public ArrayList<Location> branch;public int []weight;/*** Constructs a box bug that traces a square of a given side length* * @param length* the side length*/public MazeBug() {setColor(Color.GREEN);last = new Location(0, 0);/** initialize property*/visit = new boolean[100][100];for(int i=0;i<100;i++){for(int j=0;j<100;j++){visit[i][j]=false;}}Location loc = getLocation();branch = new ArrayList<Location>();branch.add(loc);
/** 四個方向選擇次數(shù)默認(rèn)都是1,如果第一個節(jié)點(diǎn)選擇向左,則向左次數(shù)加1,注意思考什么時(shí)候次數(shù)需要減少。*/weight = new int[4];for(int i=0;i<4;i++){weight[i]=1;}}/*** Moves to the next location of the square.*//** 選擇行走方向可以使用java的隨機(jī)數(shù)類Random。* 要注意什么時(shí)候該入棧,什么時(shí)候該出棧。* 存儲一個節(jié)點(diǎn)時(shí),注意除了存儲”該節(jié)點(diǎn)位置”和”已訪問方向”外,還要存儲”進(jìn)入節(jié)點(diǎn)的方向”,以便在前路不通時(shí)能成功返回。*/public void act() {boolean willMove = canMove();if (isEnd == true) {//to show step count when reach the goal if (hasShown == false) {String msg = stepCount.toString() + " steps";JOptionPane.showMessageDialog(null, msg);hasShown = true;}} else if (willMove) {visit[next.getRow()][next.getCol()]=true;move();//increase step count when move stepCount++;} else {if(branch.isEmpty()){branch = crossLocation.pop();Location loc=branch.get(branch.size()-1);int dir=getLocation().getDirectionToward(loc);/** 四個方向選擇次數(shù)默認(rèn)都是1,如果第一個節(jié)點(diǎn)選擇向左,則向左次數(shù)加1* 注意思考什么時(shí)候次數(shù)需要減少。* 回溯的時(shí)候次數(shù)減1*/if(dir==Location.NORTH){weight[2]--;}else if(dir==Location.EAST){weight[1]--;}else if(dir==Location.SOUTH){weight[3]--;}else if(dir==Location.WEST){weight[0]--;}}next = branch.remove(branch.size()-1);move();stepCount++;}}/*** Find all positions that can be move to.* * @param loc* the location to detect.* @return List of positions.*/public ArrayList<Location> getValid(Location loc) {Grid<Actor> gr = getGrid();if (gr == null){return null;}ArrayList<Location> valid = new ArrayList<Location>();/** 蟲子的行走方向只有東南西北四個方向,* 且在碰到迷宮出口(紅石頭)時(shí),蟲子會自動停下來。*/int[] direction ={ Location.NORTH, Location.WEST, Location.EAST, Location.SOUTH };for(int d:direction){Location neighbor= loc.getAdjacentLocation(getDirection() + d);if(gr.isValid(neighbor)){Actor a = gr.get(neighbor);if((a==null||a instanceof Flower)&&!visit[neighbor.getRow()][neighbor.getCol()]){valid.add(neighbor);}else if(a instanceof Rock){if(Color.RED.equals(a.getColor())){isEnd = true;} }}}return valid;}/** 增加方向的概率估計(jì)。五個評估成績的迷宮都有一定的方向偏向性,如圖四就有向上和向左的偏向性。* 在行走正確路徑時(shí),對四個方向的選擇次數(shù)進(jìn)行統(tǒng)計(jì),從而控制隨機(jī)選擇時(shí)選擇某個方向的概率。增加方向的概率估計(jì)后能有效地提高走迷宮的效率。* 提示:四個方向選擇次數(shù)默認(rèn)都是1,如果第一個節(jié)點(diǎn)選擇向左,則向左次數(shù)加1,注意思考什么時(shí)候次數(shù)需要減少。*/public Location directionPrediction(ArrayList<Location> locs){int dir=0;int westWeight=0,eastWeight=0,northWeight=0,southWeight=0;for(Location loc:locs){dir=getLocation().getDirectionToward(loc);if(dir==Location.NORTH){northWeight=weight[2];}else if(dir==Location.EAST){eastWeight=weight[1];}else if(dir==Location.SOUTH){southWeight=weight[3];}else if(dir==Location.WEST){westWeight=weight[0];}}int random= 1 + (int)(Math.random() * (westWeight + eastWeight + northWeight + southWeight));if (random <= westWeight) {dir = Location.WEST;weight[0]++;} else if (random <= (westWeight + eastWeight)) {dir = Location.EAST;weight[1]++;} else if (random <= (westWeight + eastWeight + northWeight)) {dir = Location.NORTH;weight[2]++;} else {dir =Location.SOUTH;weight[3]++;}Location next=null;for(Location loc:locs){if(dir==getLocation().getDirectionToward(loc)){next=loc;}}return next;}/*** Tests whether this bug can move forward into a location that is empty or* contains a flower.* * @return true if this bug can move.*/public boolean canMove() {ArrayList<Location> locs=getValid(getLocation());if(locs.isEmpty()){return false;}else{branch.add(getLocation());if(locs.size()>1){crossLocation.push(branch);branch = new ArrayList<Location>();next = directionPrediction(locs);}else{next=locs.get(0);}last = getLocation();}return true;}/*** Moves the bug forward, putting a flower into the location it previously* occupied.*//** 選擇行走方向可以使用java的隨機(jī)數(shù)類Random。* 要注意什么時(shí)候該入棧,什么時(shí)候該出棧。* 存儲一個節(jié)點(diǎn)時(shí),注意除了存儲”該節(jié)點(diǎn)位置”和”已訪問方向”外,還要存儲”進(jìn)入節(jié)點(diǎn)的方向”,以便在前路不通時(shí)能成功返回。*/public void move() {Grid<Actor> gr = getGrid();if (gr == null){return;}Location loc = getLocation();if (gr.isValid(next)) {setDirection(getLocation().getDirectionToward(next));moveTo(next);} else{removeSelfFromGrid();}Flower flower = new Flower(getColor());flower.putSelfInGrid(gr, loc);}
}
4. mazebug的readme文件
This project can be run using Eclipse. Its introduction and running screenshots are listed below. We can also use Sonar-Qube for code analysis of project files.
The overall structure of the project is as follows:
(1)MazeBugRunner.java
This Java file is the entry to the program. Running this file calls the ActorWorld methods.
In GridWorld, we can set the grid size and load the maze. And then, we can use the depth-first algorithm to get out of the maze.
(2)MazeBug.java
This file realizes the method of getting out of the maze based on depth first search and direction probability estimation.
(3)Examples
OneRoadMaze:
EasyMaze:
FinalMaze01:
FinalMaze02:
FinalMaze03;
FinalMaze04:
FinalMaze05;
5. jigsaw的工作
jigsaw(N-Puzzle)是筆者認(rèn)為整個中級實(shí)訓(xùn)最好玩的一個任務(wù),筆者以外地發(fā)現(xiàn)可以在這個小項(xiàng)目中進(jìn)行一些“煉丹工作者”的fine-tune操作。
筆者先根據(jù)要求在啟發(fā)式搜索算法的框架下,完成了簡單的廣度優(yōu)先算法:
/***(實(shí)驗(yàn)一)廣度優(yōu)先搜索算法,求指定5*5拼圖(24-數(shù)碼問題)的最優(yōu)解* 填充此函數(shù),可在Solution類中添加其他函數(shù),屬性* @param bNode - 初始狀態(tài)節(jié)點(diǎn)* @param eNode - 目標(biāo)狀態(tài)節(jié)點(diǎn)* @return 搜索成功時(shí)為true,失敗為false*/public boolean BFSearch(JigsawNode bNode, JigsawNode eNode) {this.beginJNode = new JigsawNode(bNode);this.endJNode = new JigsawNode(eNode);this.currentJNode = null;this.visitedList = new HashSet<>(1000);this.exploreList = new LinkedList<JigsawNode>();Set<JigsawNode> recordHashSet = new HashSet<>(1000);this.searchedNodesNum = 0;super.reset();final int MAX_NODE_NUM = 29000;final int DIRS = 4;exploreList.add(this.beginJNode);recordHashSet.add(this.beginJNode);while (this.searchedNodesNum < MAX_NODE_NUM && !exploreList.isEmpty()) {this.currentJNode = exploreList.peek();if (this.currentJNode.equals(eNode)) {this.getPath();break;}this.exploreList.remove();recordHashSet.remove(currentJNode);this.visitedList.add(this.currentJNode);this.searchedNodesNum++;JigsawNode[] nextNodes = new JigsawNode[]{new JigsawNode(this.currentJNode), new JigsawNode(this.currentJNode),new JigsawNode(this.currentJNode), new JigsawNode(this.currentJNode)};for (int i = 0; i < DIRS; ++i) {if (nextNodes[i].move(i) && !recordHashSet.contains(nextNodes[i]) && !this.visitedList.contains(nextNodes[i])) {exploreList.add(nextNodes[i]);recordHashSet.add(nextNodes[i]);}}}System.out.println("Jigsaw BF Search Result:");System.out.println("Begin state:" + this.getBeginJNode().toString());System.out.println("End state:" + this.getEndJNode().toString());System.out.println("Solution Path: ");System.out.println(this.getSolutionPath());System.out.println("Total number of searched nodes:" + this.searchedNodesNum);System.out.println("Depth of the current node is:" + this.getCurrentJNode().getNodeDepth());return this.isCompleted();}
然后便是為啟發(fā)式搜索算法完善估價(jià)函數(shù),在實(shí)訓(xùn)博客中有如下定義:
估價(jià)函數(shù)f(n)用來估計(jì)節(jié)點(diǎn)n的重要性,表示為:從起始節(jié)點(diǎn),經(jīng)過節(jié)點(diǎn)n,到達(dá)目標(biāo)節(jié)點(diǎn)的代價(jià)。f(n)越小,表示節(jié)點(diǎn)n越優(yōu)良,應(yīng)該優(yōu)先訪問它的鄰接節(jié)點(diǎn)。可參考的估價(jià)方法有:
①所有 放錯位的數(shù)碼 個數(shù)
②所有 放錯位的數(shù)碼與其正確位置的距離 之和
③后續(xù)節(jié)點(diǎn)不正確的數(shù)碼個數(shù)
……
可以同時(shí)使用多個估價(jià)方法,f(n) = af1(n) + bf2(n) + … 通過適當(dāng)調(diào)整權(quán)重(a,b,…),能夠加快搜索速度。
在博客的基礎(chǔ)上,筆者一共實(shí)現(xiàn)了四種估價(jià)參數(shù),并通過手動設(shè)置不同的權(quán)值來進(jìn)行fine-tune操作(下面代碼中的參數(shù)是手動測試得到的局部最優(yōu)值):
/***(Demo+實(shí)驗(yàn)二)計(jì)算并修改狀態(tài)節(jié)點(diǎn)jNode的代價(jià)估計(jì)值:f(n)* 如 f(n) = s(n). s(n)代表后續(xù)節(jié)點(diǎn)不正確的數(shù)碼個數(shù)* 此函數(shù)會改變該節(jié)點(diǎn)的estimatedValue屬性值* 修改此函數(shù),可在Solution類中添加其他函數(shù),屬性* @param jNode - 要計(jì)算代價(jià)估計(jì)值的節(jié)點(diǎn)*/public void estimateValue(JigsawNode jNode) { /** four cost*/int s = 0; int misplacedTiles = 0; int manHattan = 0; int euclidean = 0; int dimension = JigsawNode.getDimension();/** 1st cost: 后續(xù)節(jié)點(diǎn)不正確的個數(shù)*/for (int index = 1; index < dimension * dimension; index++) {if (jNode.getNodesState()[index] + 1 != jNode.getNodesState()[index + 1]) {s++;}}/** 2nd cost: 所有放錯位的數(shù)碼個數(shù)*/for(int index = 1; index <= dimension*dimension; index++){if(jNode.getNodesState()[index] != index)misplacedTiles++;}/** 3rh and 4th cost: 曼哈頓距離 & 歐幾里得距離*/for (int i = 1; i <= dimension * dimension; ++i) {if (jNode.getNodesState()[i] != 0) {for (int j = 1; j <= dimension * dimension; ++j) {if (jNode.getNodesState()[i] == this.endJNode.getNodesState()[j]) {int startX = (i - 1) / dimension;int startY = (i - 1) % dimension;int endX = (j - 1) / dimension;int endY = (j - 1) % dimension;manHattan += Math.abs(startX - endX) + Math.abs(startY - endY);euclidean += Math.pow(startX - endX, 2) + Math.pow(startY - endY, 2);break;}}}}//fine-tune to set the locally optimal weightsjNode.setEstimatedValue(8 * s + 4 * manHattan + 2 * euclidean + 1 * misplacedTiles);}
在筆者愉快的調(diào)參過程中,筆者經(jīng)過多次測試調(diào)優(yōu)后,發(fā)現(xiàn)該項(xiàng)目的最高得分score為7,最低得分score為3(權(quán)值均為0),調(diào)參過程果然還是挺有趣的~
四、總結(jié)和心得體會
在本次中級實(shí)訓(xùn)臨近結(jié)束時(shí),不禁感慨個人的實(shí)力還是有限的,一個人不可能在0經(jīng)驗(yàn)的情況下無師自通,還是得多多學(xué)習(xí)和多多請教。
與大一的初級實(shí)現(xiàn)相比,中級實(shí)訓(xùn)的代碼量看似多了不少,但實(shí)際上難度應(yīng)該不在于代碼的編寫,而在于項(xiàng)目框架的理解,在掌握了基本的工具與框架之后,代碼的編寫簡直是如魚得水。但與此同時(shí),每個階段的問答題,還是讓我耗費(fèi)了更多的精力,當(dāng)然也因此認(rèn)識到自己項(xiàng)目開發(fā)的不足,比如考慮問題的角度不夠細(xì)致,單元測試有所缺陷,上交TA檢查代碼前沒有做好充足的準(zhǔn)備而突發(fā)BUG等等。這些都讓我對軟件開發(fā)流程有了更深刻的理解。
在整個中級實(shí)訓(xùn)過程中,我最喜歡的是擴(kuò)展任務(wù)部分,這讓我有一種真正學(xué)到一些有趣的知識的感覺,而不用像基本任務(wù)那樣“填鴨式”地完成編程任務(wù)。當(dāng)然,這也有可能是因?yàn)椴恍枰裰澳菢訉χa完成問答題。但總而言之,在擴(kuò)展任務(wù)部分進(jìn)行飛馬行空的編程,寫寫DFS和BFS,寫寫方向預(yù)測和進(jìn)行快樂的調(diào)參,這些經(jīng)歷都是不可多得的。
最后,這次實(shí)訓(xùn)總結(jié)得到了很多人的幫助,如TA、舍友和往屆師兄師姐等,希望這篇略長的實(shí)訓(xùn)總結(jié)報(bào)告,能為往后的師弟師妹們獻(xiàn)上一分薄力。
謝謝堅(jiān)持完成中級實(shí)訓(xùn)任務(wù)的自己,日后還請多多加油。
總結(jié)
- 上一篇: CentOS Docker安装配置部署G
- 下一篇: 基于Golang的简单web服务程序开发