Capt.Murasa 发表于 2014-9-1 18:39:10

用VB自編的彷製遊戲(附遊戲程式,遊戲本體,程式碼)

本帖最后由 Capt.Murasa 于 2014-9-1 18:39 编辑

此遊戲的原形是經典網遊Neopets的「移形大法」東方大法好。我還未入東方,是小學生的時候,就是碰Neopets。
彷製遊戲載點於最底

遊戲網址(需注册):
http://www.neopets.com/medieval/shapeshifter_index.phtml








本彷製遊戲截圖:

(渣遊戲介面......要嘖的請輕嘖)



概括的遊戲規則:使用全部的紅色拼圖(本遊戲為綠色)來轉換方格,令全部方塊的圖案變成為劍(本遊戲為α)。
















詳細的程式碼駐解,在程式碼上寫上,...嗯...駐解的字數也不少@@

不過倒是想在這裡談談VB裡面的 Enum(列舉),Interface(介面),MustInherit(抽象類別)

一想到要編寫 不同的綠色拼圖 的程式碼,就會立即想到,他們會是一大堆同樣性質,但細節不同的型別(又不可以說是物件object)
直覺判斷,要開始考慮此三項東西。畢竟我未曾把他們派上用場,都只是由教學書認識他們。



列舉(Enum) 是什麼呢?給我的趕腳就是一個集合,是一堆整數的集合。要存取資訊,就得要輸入字串,但是輸出,永遠都是數值,別無他選。唉!!!!!!!!???? 難道我所想象的拼圖單靠數值就能夠表示得到出來!?真是嗎......
到底要有如處理方法......再想下去就一額汗.....{:36:}


介面(Interface)........唉呀.....其實我最搞不懂就是這個......{:ml41:}
據教本說明,介面就類似一份合同,上面規定著去實作此介面的型別(簽署者)必須具有何等屬性﹑方法﹑委派(delegate)等等,但此介面不會提供任何有實際行動的程式碼(那有毛管用麼!?{:ml46:}),有實際行動的程式碼則要由 簽署者親自補上,一個也不能少,即一個介面實作者 必定會有與其他人共同擁有的方法,只不過,實作方式不同而己......
就像小鳥和飛機一樣能飛,只差於飛行方式。
將拼圖寫成型別....是可以的...雖然有大量程式碼會copy and paste,但是編出來.....就條理和篇幅方面,趕腳似乎不及列舉呢.......


抽象類別(abtract class),是我最終採用的選擇。若果拼圖是類別(class)的話,抽象類別(程式裡面是puzzle)可以作為各種拼圖的基底類別(父類別),各種拼圖為 其子類別。
各種不同的拼圖 有共同且一致的方法(Method),亦有其各自不同的方法 或者 共同方法,但有不同的實作方式(出現在父類別中,有MustOverride修飾詞)。各種拼圖(子類別)可以呼叫puzzle(父類別)的方法,而且此方法對各種拼圖來說,是一致的。此舉與介面(Interface)相比,減少了程式的篇幅。至於共同擁有的方法,但不同的實作方式【聽說這是介面(Interface)的賣點其一】,只要用在父類別裡面宣告該方法加上MustOverride修飾詞即可。至於子類別獨立擁有的方法,就直接在子類別中宣告就行了。


而且,我留意到 抽象類別有著一點泛型(generic)的性質。留意到下面的範例:

Public Class consumeShapes
    Public Sub makeShapes()
      Dim shape1, shape2 As shape
       shape1 = New circle
      shape2 = New square
    End Sub
End Class


Public MustInherit Class shape
    Public acrossLine As Double
    Public MustOverride Function area() As Double
End Class
Public Class circle : Inherits shape
    Public Overrides Function area() As Double
      Return Math.PI * acrossLine
    End Function
End Class
Public Class square : Inherits shape
    Public Overrides Function area() As Double
      Return acrossLine * acrossLine
    End Function
End Class




抽象類別似乎可以當作一個型別變數,容許指向於他的變數,直接變成其子類別的物件(object)。如果再配搭上陣列的話,陣列可以變成一個集合,方便存取不同的子類別物件(各種拼圖)。唉!?這不是能夠實作為一個列舉型別嗎?

若抽象類別(abtract class)加以配搭的話,似乎可以視作為介乎 列舉(Enum)和介面(Interface)的東西吶.........
嗯,想起來,抽象類別也挺有本事{:20:}........最初認為他沒什麼一流的特別功能{:18:}..............好.....我貼貼服服的跪了{:07:}



總之,我想像不了列舉(Enum),在這裡有著什麼用途{:ml51:}
或許笨人入世未深.......
至於介面(Interface),從抽象類別取經後,現在也可以研究一下
我期待列舉(Enum)和介面(Interface)去實作拼圖類別﹑集合的方式
這就有勞各位程編觸咯{:33:}





而彷製的這個遊戲吶,模式大概與原作相似,關卡就是由本人「設計」。實際上,關卡設計是非常容易,但要通關的就非常吃力。
....咳嗯......話說這遊戲都是笨人第一個自主編寫 (不按照教學書,但有參考微軟範例) 的彷製遊戲,而且我不是主修程式編寫。若是上述見解有誤﹑遊戲程式有bug或者程式碼太冗長﹑關卡太難﹑遊戲介面爛....等等......請輕嘖

討論關卡,關卡其實還可以有發展的空間。有關方格,我只是造了3x3 的方格,而原版的那個在更高level數目,就會有3x4的方格,而且圖片數量亦會變多。拚圖也會有更多種形狀。






game,程式載點:

attach://69762.rar

程式碼(textbook) :

attach://69784.rar

載點又傲嬌了=.=....求修正








drzzm32 发表于 2014-9-1 18:46:39

本帖最后由 drzzm32 于 2014-9-2 18:21 编辑

直接复制链接打开即可。。。貌似论坛抽了?

.NET Framework 4.0需求

PS:为何我看不懂这个游戏啊。

gqyyyy 发表于 2014-9-1 21:50:59

VB大法好{:09:}

ofz 发表于 2014-9-2 16:38:56

hihihi,楼主你好我又过来玩了~

看到这种 A->B->A 的点阵和变换,ofz 第一想到的算法是二进制编码然后做位运算呢。因为闲着就跑过去注册玩了一下,发现核心算法的逻辑确实很简单,于是就有了下面这些仿制品:
3x3 点阵 http://jsfiddle.net/cm3vzfog/1/

4x3 点阵 http://jsfiddle.net/cm3vzfog/2/

稍微有点吓人的 6x5 点阵 http://jsfiddle.net/cm3vzfog/3/



楼主在原帖里讨论的 Interface,MustInherit 等概念,更多的是属于“设计模式”范畴的东西。关于逻辑和算法,我来补充几点吧


1. 特定位置的位翻转:使用 mask 做异或运算
异或运算的基本规则非常简单,先看一位二进制的情况(^ 表示异或算符 xor)
bit ^ mask = bit2
1 ^ 0 = 1 (mask 为 0,不翻转)
0 ^ 0 = 0 (mask 为 0,不翻转)
1 ^ 1 = 0 (mask 为 1,1 翻转为 0)
0 ^ 1 = 1 (mask 为 1,0 翻转为 1)推广到多位二进制数的时候,只要对齐再各自对每一位做运算就好了,像
num ^ mask = num2
010 ^ 100 = 110 (只翻转第一位)
010 ^ 110 = 100 (翻转一二位)
就是这么简单的规则哟

接下来,异或运算如何与游戏联系起来呢?拿这张图来说

只看第一行: OXX 可以编码为 011
如果要放一个 1x1 的 mask 到左上角使点阵翻转,用下面式子来表示

011 ^ 100 = 111 (变成了 XXX)
放到上面中间就是
011 ^ 010 = 001 (OOX)
放到上面右边则是
011 ^ 001 = 010 (OXO)
其他行也类似。这就是游戏的基本规则。

2. 从二进制数到点阵:填 0 移位

上面使用的是 1x1 的 mask,只需要考虑一行的情况,如果是如上图红色标注的 2x2 mask 应该如何处理呢?这边用到了一点技巧
首先考虑整个 3x3 点阵,需要用三个二进制数编码 010,001 和 110;然后那个 2x2 mask 需要用两个二进制数 11 和 10,这几个数是不好直接做异或运算的。我使用的方法是把 mask 右边和下面补上 0,填充为和源点阵一样的大小(3x3),即
11   ->    110
10   ->    100
       ->    000
由于异或是位与位之间的运算,所以直接把三个二进制数合并成一个二进制数计算也是没问题的
合并后的点阵:010001110
合并后的mask:110100000
翻转后结果:010001110 ^ 110100000 = 100101110(第一行XOO,第二行XOX,第三行XXO)

好像有什么地方不对?是的,这样子将两个数对齐作异或,相当于直接把 mask 放到了点阵的左上角。为了得到上图所示的结果,我们需要把点阵与 mask 错开几位再计算。这可以用移位运算符来做(左移 << 和右移 >>)。因为是 3x3 的点阵,将 mask 向下移一行相当于把 mask 向右移 3 位
翻转后结果:010000110 ^ (110100000 >> 3) = 010111010(OXO,XXX 和 OXO)

如果到这一步能够理解的话,相信你也就可以把整个游戏的核心逻辑写出来了(以下是 javascript)
function applyFlip(num, mask, i, j) {
    return num ^ (mask >> (i * M + j))
}

只有一行代码的函数?嗯,只需要一行就够了

3. 出题
结合上面的解释,用数学逻辑来表述题面的话应该是这样:给你一个二进制数 X 和几个 mask (A, B, C...),使用移位和异或运算使
X ^ (A >> a) ^ (B >> b) ^ ... = 所有位上全是 0 或 全是 1 的二进制数(即 0 或 ~0)
求解的过程本质上就是在求 a, b, c... 这些系数

在出题的时候,重点是要得到一个合理的 X。这边需要提到异或运算的两个性质
1) 异或运算不分先后:X ^ A ^ B = X ^ B ^ A
2) 两次与同一个数异或将使其效果抵消:X ^ A ^ A = X
假设我们从 0 开始(~0 也一样),随机找几个 mask 做异或,可以得到
0 ^ A ^ B ^ ... = X
根据上面两个性质,把 X 再做相同的异或运算(不分顺序),肯定能够重新得到 0
X ^ A ^ B ^ ... = 0
相信你已经想到了,出题的方法就是从 0 (或~0)开始,随机找几个 mask (A, B, C...) 并加上 随机的偏移 (a, b, c...) 做异或运算,最后把得到的 X 和用到的 A, B, C... 告诉做题的人,让他们去猜 a, b, c... 就可以啦
注意:a, b, c...的选择虽然随机,但是有一定的限制范围,并非随便找一个数就可以的(这个限制将是暴力求解的基础)

4. 求解
这边我只讲一下暴力求解的思路。前面提到偏移量 a, b, c... 的限制从何而来?

现在有一个 2x2 的 mask,把它放到 3x3 的点阵里,有几种放法?答案是 (3 - 2 + 1) * (3 - 2 + 1) = 4,mask 的位偏移只能是 0, 1, 3, 4 这几个数;类似的,把一个 1x3 的 mask 放到 3x3 的点阵里,可能的偏移只有(3 - 1 + 1) * (3 - 3 + 1) = 3 种,即 0, 1, 2 这三个值;即使是 1x1 的 mask,最多也只有 9 种放法。由于这些值都是有限的,我们可以尝试穷尽这些所有的可能性,只要检测到结果是 0 或者 ~0 就算成功了。假设是 3x3 的点阵,有 5 个 mask,那么最差的情况也就是计算 9 的 5 次方(59049)种可能性而已哦(而且最差的情况对人类反倒是最简单的说


总结
因为一直不停地有人来找我,写这个帖子的时间比我抠代码的时间多了好几倍...不想总结啦啊啊啊

winzjjj 发表于 2014-9-12 19:46:12

楼上大触~
vb6默默路过

q1437919259 发表于 2014-9-13 20:44:56

下来看看先...

yokar 发表于 2014-9-14 02:44:28

移形大法啊...
到后面的关卡全都是人类无法做到的恐怖难度所以游戏设计者这样设计真的没问题吗?
页: [1]
查看完整版本: 用VB自編的彷製遊戲(附遊戲程式,遊戲本體,程式碼)