大赛小组人数区间3~5人,工作简单分配的话就是编码,美工和文档。小队五人也是按照这个规则进行的划分。但是问题很快就暴露出来了,作为小队队长的我,无论是项目设计经验还是工作安分配验都严重匮乏(我对项目的理解程度,只是在兴趣使然的情况下拜读了FrederickP.Brooks.Jr.《人月神话》),以至于在后续的工作中,我自己完全沦为了一名伪devops(开发,运维,测试于一体的职位),这直接导致了我整个暑假都在战战兢兢码代码的过程中度过。
介于我汗与泪的教训,在这里引入第一个公理:
一个优秀的指挥者胜过三个优秀的程序员(可以推广为程序员应该学会用架构的思维看待每一个项目)(抱紧对的大腿等于成功一半) 。
指挥者只有对项目划分、前端设计、后台搭建、框架使用有了足够系统的学习,且对算法的理解达到一定深度,对一个项目的每一部分有一个大致的、关键的了解,才能在很短时间内将每项子任务精确到个人,于是即便他不参加到项目进程当中,工作也会有条不紊的开展起来。
在敲定作品主题是手机游戏之后,小组开始了漫长而又坎坷的摸索历程。
在最终的总结中我写到:
“我们挑选了两款当今使用较多的游戏设计特别是2D游戏设计领域的软件——cocos2D-x与unity3D。
由于许多人们耳熟能详的游戏都出自cocos之手,我们欣然优先选择了cocos。但后期的学习中,我们渐渐发现,cocos的设计较为死板,且可利用资源较少,同时由于网上搜寻到的资料是推荐在mac上运行的,这并不符合我们项目小组的设备现状。
于是我们转而选择了现今游戏开发走在前列的unity。Unity作为3D游戏设计的扛把子,在2D与3D游戏领域都有着受欢迎的代表作品,尽管unity涉足2D时间较晚,但是由于其3D投影2D的思路,使得它在2D游戏领域发展极快。
可视化界面,优秀的资源调配,游戏过程实时校验,以及项目最后的android导出,堪称一条龙服务。”
要在有限的时间内要创造出最高的价值,就意味着我们不应当浪费时间在许多无意义的工作中:
虽然对于问题的摸索是难能可贵的经历,但在严格规定deadline的竞赛中,摸索的代价却是最为珍贵的时间和人力,这是任何一个队伍都承受不起的。
所以,认清浪费的本质,尽可能避免错误的深入与摸索,急流勇退并非明哲保身,而是为了以退为进。
软件设计大赛的得分点其实主要还是在创新。相较于程序大牛,我们的代码无论是整体性能还是结构美观都相差太多。能够打动评审的,是别出心裁的思路以及天马行空的想象。
这也意味着演示文档变成了很重要的一项工作,只有在演示过程中(无论视频还是PPT)都力争先声夺人,力求给评审留下好的第一印象。
这并不意味着只需要功利的只关注界面设计,好的用户体验也包括响应时间,作品的易学性,正确性等等,我们需要的,只是一个能激起他人探索欲望的契机罢了。
万里挑一的灵魂亦需好看的皮囊包装
好的作品永远需要需要足够优秀的外在来撩动用户的好奇,促使他们对真正有趣的内在进行探索。
一个项目小组成果的优劣直接取决于小组的代码水准,默契程度,分工合理与否,以及设计创意提出。但是最重要的部分还是取决于是否团结,人终归还是一个团队的基础。
必须承认,参与这样的程序设计是对个人能力的一种考验,也是对自己的编程思想以及理论实践能力的拷问。
拢总算来,收获良多。
另附:
加权创建五子棋AI代码:
class AI
{
// 15*15共有572种五子连珠的可能性
const int MaxFiveChainCount = 572;
//玩家的可能性
bool[,,] _ptable = new bool[Board.CrossCount, Board.CrossCount, MaxFiveChainCount];
//电脑的可能性
bool[, ,] _ctable = new bool[Board.CrossCount, Board.CrossCount, MaxFiveChainCount];
//记录2位玩家所有可能的连珠数,-1则为永远无法5连珠
int[,] _win = new int[2, MaxFiveChainCount];
//记录每格的分值
int[,] _cgrades = new int[Board.CrossCount, Board.CrossCount];
int[,] _pgrades = new int[Board.CrossCount, Board.CrossCount];
//记录棋盘
int[,] _board = new int[Board.CrossCount, Board.CrossCount];
int _cgrade, _pgrade;
int _icount, _m, _n;
int _mat, _nat, _mde, _nde;
public AI( )
{
for ( int i = 0;i= 4; j--)
{
for (int k = 0; k < BoardModel.WinChessCount; k++)
{
_ptable[j - k, i + k, _icount] = true;
_ctable[j - k, i + k, _icount] = true;
}
_icount++;
}
}
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < MaxFiveChainCount; j++)
{
_win[i, j] = 0;
}
}
_icount = 0;
}
void CalcScore( )
{
_cgrade = 0;
_pgrade = 0;
_board[_m, _n] = 1;//电脑下子位置
for (int i = 0; i < MaxFiveChainCount; i++)
{
if (_ctable[_m, _n, i] && _win[0, i] != -1)
{
_win[0, i]++;//给白子的所有五连子可能的加载当前连子数
}
if (_ptable[_m, _n, i])
{
_ptable[_m, _n, i] = false;
_win[1, i] = -1;
}
}
}
void CalcCore( )
{
//遍历棋盘上的所有坐标
for (int i = 0; i < Board.CrossCount; i++)
{
for (int j = 0; j < Board.CrossCount; j++)
{
//该坐标的黑子奖励积分清零
_pgrades[i, j] = 0;
//在还没下棋子的地方遍历
if (_board[i, j] == 0)
{
//遍历该棋盘可落子点上的黑子所有权值的连子情况,并给该落子点加上相应奖励分
for (int k = 0; k < MaxFiveChainCount; k++)
{
if (_ptable[i, j, k])
{
switch (_win[1, k])
{
case 1://一连子
_pgrades[i, j] += 5;
break;
case 2://两连子
_pgrades[i, j] += 50;
break;
case 3://三连子
_pgrades[i, j] += 180;
break;
case 4://四连子
_pgrades[i, j] += 400;
break;
}
}
}
_cgrades[i, j] = 0;//该坐标的白子的奖励积分清零
if (_board[i, j] == 0)//在还没下棋子的地方遍历
{
//遍历该棋盘可落子点上的白子所有权值的连子情况,并给该落子点加上相应奖励分
for (int k = 0; k < MaxFiveChainCount; k++)
{
if (_ctable[i, j, k])
{
switch (_win[0, k])
{
case 1://一连子
_cgrades[i, j] += 5;
break;
case 2: //两连子
_cgrades[i, j] += 52;
break;
case 3://三连子
_cgrades[i, j] += 130;
break;
case 4: //四连子
_cgrades[i, j] += 10000;
break;
}
}
}
}
}
}
}
}
// AI计算输出, 需要玩家走过的点
public void ComputerDo(int playerX, int playerY, out int finalX, out int finalY )
{
setPlayerPiece(playerX, playerY);
CalcCore();
for (int i = 0; i < Board.CrossCount; i++)
{
for (int j = 0; j < Board.CrossCount; j++)
{
//找出棋盘上可落子点的黑子白子的各自最大权值,找出各自的最佳落子点
if (_board[i, j] == 0)
{
if (_cgrades[i, j] >= _cgrade)
{
_cgrade = _cgrades[i, j];
_mat = i;
_nat = j;
}
if (_pgrades[i, j] >= _pgrade)
{
_pgrade = _pgrades[i, j];
_mde = i;
_nde = j;
}
}
}
}
//如果白子的最佳落子点的权值比黑子的最佳落子点权值大,则电脑的最佳落子点为白子的最佳落子点,否则相反
if (_cgrade >= _pgrade)
{
_m = _mat;
_n = _nat;
}
else
{
_m = _mde;
_n = _nde;
}
CalcScore();
finalX = _m;
finalY = _n;
}
void setPlayerPiece( int playerX, int playerY )
{
int m = playerX;
int n = playerY;
if ( _board[m, n ] == 0 )
{
_board[m, n] = 2;
for( int i = 0;i