├── .gitignore
├── README.md
├── app.js
├── app.json
├── app.wxss
└── pages
    ├── images
        └── icon
        │   ├── emoticon_happy.png
        │   └── emoticon_sad.png
    └── index
        ├── index.js
        ├── index.wxml
        └── index.wxss
/.gitignore:
--------------------------------------------------------------------------------
1 | .DB_Store
2 | *.swp
3 | *.log
4 | public/
5 | .deploy*/
6 | 
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | 微信小程序这几天很火,9号正式发布,今天19号过了10天,到现在略有降温的趋势。个大厂商对微信小程序的态度很不看好,最激进的要数[罗辑思维],直接下架了自己的[得到APP]。滴滴、京东等APP也只保留了最精简的功能,某些担心过于完善的功能会抢夺APP本身的用户。不过这也印证了小程序的初衷,小而精、用完即走。(并不否认腾讯微信平台确实流氓的事实)
 2 | 
 3 | >PS:墨迹天气也开发了微信小程序版本,并且在1月9号第一时间发布,欢迎使用。
 4 | 
 5 | 作为技术人员,怎能不亲手试试小程序开发呢?扫雷 Minesweeper
 6 | 
 7 | ## 实现思路:
 8 | 
 9 | * 如果不了解扫雷的游戏规则,请自行 Google
10 | * 绘制一张 10*10 的地图,并且标注地雷和非地雷,并且需要用数字表示每个点周围地雷的数量
11 | * 用 mineMap[x][y] 表示一个点位
12 |     * mineMap[x][y] < 0 (代码中 == -1) 初始状态
13 |     * 0 < mineMap[x][y] < 9 该点周围地雷的数量
14 |     * mineMap[x][y] == 9 表示该点是地雷
15 |     * mineMap[x][y] > 9 (代码中 == 10)表示该点插旗
16 | 
17 | ## Github 
18 | * https://github.com/jixiaod/wechat-app-minesweeper
19 | 
20 | ## 如何跑起来?
21 | * 下载&安装微信小程序开发工具 
22 | * 配置 wechat-app-minesweeper 代码目录到开发工具即可
23 | 
24 | ## 玩法
25 | * 需要注意的是,由于没有鼠标右键标记Flag,简单做了 Flag 切换,开启后,方可标记Flag
26 | * 最初的想法是通过 tap 和 longtap 来区分扫雷和Flag,但是开发工具不能区分这俩个事件,只能作罢
27 | 
28 | 
29 | 
30 | 
31 | 
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 |   onLaunch: function () {}
4 | })
5 | 
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "pages":[
 3 |     "pages/index/index"
 4 |   ],
 5 |   "window":{
 6 |     "backgroundTextStyle":"light",
 7 |     "navigationBarBackgroundColor": "#fff",
 8 |     "navigationBarTitleText": "Minesweeper",
 9 |     "navigationBarTextStyle":"black"
10 |   }
11 | }
12 | 
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
 1 | /**app.wxss**/
 2 | .container {
 3 |   height: 100%;
 4 |   display: flex;
 5 |   flex-direction: column;
 6 |   align-items: center;
 7 |   justify-content: space-between;
 8 |   padding: 200rpx 0;
 9 |   box-sizing: border-box;
10 | } 
11 | 
--------------------------------------------------------------------------------
/pages/images/icon/emoticon_happy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jixiaod/wechat-app-minesweeper/b29f11f5fdfff090beb2dc0dd755b0781eba3c57/pages/images/icon/emoticon_happy.png
--------------------------------------------------------------------------------
/pages/images/icon/emoticon_sad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jixiaod/wechat-app-minesweeper/b29f11f5fdfff090beb2dc0dd755b0781eba3c57/pages/images/icon/emoticon_sad.png
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
  1 | //index.js
  2 | //获取应用实例
  3 | var app = getApp()
  4 | 
  5 | Page({
  6 | 
  7 |     data: {
  8 |         mineMap: {},
  9 |         timesGo: 0,
 10 |         minesLeft: 0
 11 |     },
 12 | 
 13 |     mineMap: {},
 14 |     mineMapMapping: {},
 15 |     rowCount: 8,
 16 |     colCount: 8,
 17 |     mineCount: 8,
 18 |     minMineCount: 8,
 19 |     maxMineCount: 20,
 20 |     minesLeft: 0,
 21 |     timesGo: 0,
 22 |     timeInterval: null,
 23 |     flagOn: false,
 24 |     flags: 0,
 25 |     endOfTheGame: false,
 26 |     safeMinesGo: 0,
 27 | 
 28 |     onLoad: function() {
 29 | 
 30 |         this.setData({
 31 |             minesLeft: 0,
 32 |             timesGo: 0
 33 |         });
 34 |         this.drawMineField();
 35 |         this.setData({
 36 |             buttionText: 'START'
 37 |         })
 38 | 
 39 |     },
 40 | 
 41 |     setGame: function() {
 42 | 
 43 |         this.drawMineField();
 44 |         this.createMinesMap();
 45 |         this.setMinesLeft();
 46 |         this.timeGoReset();
 47 |         this.timeGoClock();
 48 |         this.endOfTheGame = false;
 49 |         this.safeMinesGo = 0;
 50 |         this.setData({
 51 |             buttionText: 'RESTART'
 52 |         })
 53 | 
 54 |     },
 55 | 
 56 |     setMinesLeft: function() {
 57 |         this.minesLeft = this.mineCount;
 58 |         this.setData({minesLeft: this.minesLeft});
 59 |     },
 60 | 
 61 |     timeGoClock: function() {
 62 |         var self = this;
 63 |         this.timeInterval = setInterval(function () {
 64 |             // console.log(self.data.timesGo);
 65 |             self.timesGo = self.timesGo + 1;
 66 |             self.setData({timesGo: self.timesGo});
 67 |             
 68 |         }, 1000);
 69 |     },
 70 | 
 71 |     timeGoStop: function() {
 72 |     
 73 |         clearInterval(this.timeInterval);
 74 |     },
 75 | 
 76 |     timeGoReset: function() {
 77 |         clearInterval(this.timeInterval);
 78 |         this.timesGo = 0;
 79 |         this.setData({timesGo: this.timesGo});
 80 |     },
 81 | 
 82 |     createMinesMap: function() {
 83 | 
 84 |         var tmpMineMap = {};
 85 |         // initalize mine map with 0.
 86 |         for (var row = 0; row < this.rowCount; row++) {
 87 | 
 88 |             tmpMineMap[row] = [];
 89 |             for (var col = 0; col < this.colCount; col++) {
 90 | 
 91 |                 tmpMineMap[row][col] = 0;
 92 |             }
 93 |         }
 94 |          //console.log(tmpMineMap);
 95 |         
 96 |         // laying mines with 9
 97 |         this.mineCount = this.rangeRandom(this.minMineCount, this.maxMineCount);
 98 | 
 99 |         var tmpCount = this.mineCount;
100 |         //console.log("Mine count: ", tmpCount);
101 |         while (tmpCount > 0) {
102 | 
103 |             var row = this.rangeRandom(0, this.rowCount - 1);
104 |             var col = this.rangeRandom(0, this.colCount - 1);
105 | 
106 |             if (tmpMineMap[row][col] != 9) {
107 | 
108 |                 tmpMineMap[row][col] = 9;
109 |                 tmpCount--;
110 |             }
111 |         }
112 | 
113 |         // calculate numbers around mines.
114 |         for (var row = 0; row < this.rowCount; row++) {
115 |             for (var col = 0; col < this.colCount; col++) {
116 |                 var startRow = row - 1;
117 |                 var startCol = col - 1;
118 |                 //console.log("check====== r" +startRow +"c"+startCol );
119 |                 for (var r = row-1; r < row+2; r++) {
120 |                     for (var c = col-1; c < col+2; c++) {
121 |                         //console.log("go: r"+r+":c"+c);
122 |                         if (c >= 0 && c < this.colCount
123 |                             && r >= 0 && r < this.rowCount
124 |                         && !(r === row && c === col) 
125 |                         && tmpMineMap[r][c] == 9 
126 |                         && tmpMineMap[row][col] != 9) {
127 |                             tmpMineMap[row][col]++;
128 |                         }
129 |                     }
130 |                 }
131 |             }
132 |         }
133 |         this.mineMapMapping = tmpMineMap;
134 |     },
135 | 
136 |     drawMineField: function() {
137 | 
138 |         var tmpMineMap = {};
139 |         for (var row = 0; row < this.rowCount; row++) {
140 | 
141 |             tmpMineMap[row] = [];
142 |             for (var col = 0; col < this.colCount; col++) {
143 | 
144 |                 tmpMineMap[row][col] = -1;
145 |             }
146 |         }
147 |         this.mineMap = tmpMineMap;
148 |         //console.log(this.mineMap);
149 | 
150 |         this.setData({
151 |             mineMap: this.mineMap 
152 |         })
153 | 
154 |     },
155 | 
156 |     demining: function(event) {
157 | 
158 |         if (JSON.stringify(this.mineMapMapping) == "{}") return;
159 | 
160 | 
161 |         var x = parseInt(event.target.dataset.x);
162 |         var y = parseInt(event.target.dataset.y);
163 |         var value = parseInt(event.target.dataset.value);
164 |         //console.log("value:" + value +" x:"+x +" y:"+y);
165 | 
166 |         //flag this field as mine.
167 |         if (this.flagOn) {
168 | 
169 |             this.flag(x, y, value);
170 |             return;
171 |         }
172 | 
173 |         // if field has been opened, return.
174 |         if (value > 0) return;
175 |         
176 |         var valueMapping = this.mineMapMapping[x][y];
177 |         //console.log(this.mineMapMapping);
178 |         //console.log(valueMapping);
179 | 
180 |         if (valueMapping < 9) {
181 |             this.mineMap[x][y] = valueMapping;
182 |             this.setData({mineMap: this.mineMap});
183 |             this.safeMinesGo++;
184 |             console.log("Safe mine go: " + this.safeMinesGo);
185 |             if ((this.safeMinesGo + this.mineCount) == (this.rowCount * this.colCount)) {
186 |                 this.success();
187 |             }
188 |         }
189 | 
190 |         // When digg the mine.
191 |         if (valueMapping == 9) {
192 |             this.failed();
193 |         }
194 | 
195 |         // Open the fields with 0 mines arround.
196 |         if (valueMapping == 0) {
197 | 
198 |             this.openZeroArround(x, y);
199 |             this.setData({mineMap:this.mineMap});
200 |         }
201 |     },
202 | 
203 |     success: function() {
204 | 
205 |         wx.showToast({
206 |             title: 'Good Job !',
207 |             image: '../images/icon/emoticon_happy.png',
208 |             duration: 3000
209 |         })
210 |         this.timeGoStop();
211 |         this.endOfTheGame = true;
212 |     },
213 | 
214 |     failed: function() {
215 |         wx.showToast({
216 |             title: 'Bomb !!!',
217 |             image: '../images/icon/emoticon_sad.png',
218 |             mask: true,
219 |             duration: 3000
220 |         })
221 | 
222 |         this.showAll();
223 |         this.timeGoStop();
224 |         this.endOfTheGame = true;
225 |     },
226 | 
227 |     // Open the fields arround 0 field recursively.
228 |     openZeroArround: function(row, col) {
229 |         //console.log("click" + row + " " + col)
230 |         for (var r = (row-1); r < (row+2); r++) {
231 |             for (var c = (col-1); c < (col+2); c++) {
232 |                 //console.log("go: r"+r+":c"+c);
233 |                 if (r >= 0 && r < this.rowCount
234 |                     && c >= 0 && c < this.colCount
235 |                 && !(r === row && c === col) 
236 |                 && this.mineMap[r][c] < 0) {
237 | 
238 |                     this.mineMap[r][c] = this.mineMapMapping[r][c];
239 |                     this.safeMinesGo++;
240 | 
241 |                     if (this.mineMapMapping[r][c] == 0) {
242 |                         this.openZeroArround(r, c);
243 |                     }
244 | 
245 |                 }
246 |             }
247 |         }
248 |         console.log("Safe mine go: " + this.safeMinesGo);
249 |         if ((this.safeMinesGo + this.mineCount) == (this.rowCount * this.colCount)) {
250 |             this.success();
251 |         }
252 | 
253 |     },
254 | 
255 |     flagSwitch: function(e) {
256 | 
257 |         if (e.detail.value) {
258 | 
259 |             this.flagOn = true;
260 |         } else {
261 | 
262 |             this.flagOn = false;
263 |         }
264 |     },
265 | 
266 |     flag: function(x, y, value) {
267 | 
268 |         if (value > 0 && value < 10) return;
269 | 
270 |         // if flaged already, set the original state.
271 |         if (value == 10) {
272 | 
273 |             this.pullUpFlag(x, y);
274 |             return;
275 |         }
276 | 
277 |         if (this.minesLeft <= 0) return;
278 | 
279 |         this.minesLeft = this.minesLeft - 1;
280 |         this.mineMap[x][y] = 10;
281 | 
282 |         this.setData({mineMap: this.mineMap, minesLeft: this.minesLeft});
283 |     },
284 | 
285 |     pullUpFlag: function(x, y) {
286 | 
287 |         if (this.minesLeft < this.mineCount) {
288 |             this.minesLeft = this.minesLeft + 1;
289 |         }
290 |         this.mineMap[x][y] = -1;
291 |         this.setData({mineMap: this.mineMap, minesLeft: this.minesLeft});
292 |     },
293 | 
294 |     rangeRandom: function(x, y) {
295 |         var z = y - x + 1;
296 |         return Math.floor(Math.random() * z + x);
297 |     }, 
298 | 
299 |     showAll: function() {
300 |         this.mineMap = this.mineMapMapping;
301 |         this.setData({mineMap: this.mineMap});
302 |     }
303 | 
304 | });
305 | 
306 | 
307 | 
308 | 
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
 1 | 
 2 |     
 6 |     
 7 |         
10 |     
11 |     
19 | 
20 | 
21 |     - 点击 START 开始游戏,RESTART 重新开始\n- Switch 切换标志插旗
22 | 
23 | 
24 | 
25 | 
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
 1 | page {
 2 |     width: 750rpx;
 3 |     height: 100%;
 4 |     font-family: -apple-system-font, Helvetica Neue, Helvetica, sans-serif;
 5 | }
 6 | 
 7 | .wrapper {
 8 |     padding: 5px;
 9 | }
10 | 
11 | .flex-container {
12 |     padding: 0;
13 |     margin: 0 auto;
14 |     list-style: none;
15 |     display: flex;
16 |     flex-flow: row;
17 |     justify-content: space-around;
18 |     line-height:1em;
19 |     width: 90%;
20 | }
21 | 
22 | .flex-item {
23 |     background: #E6E9ED;
24 |     margin: 1px;
25 |     color: white;
26 |     font-weight: bold;
27 |     font-size: 14px;
28 |     text-align: center;
29 |     flex: 1 0 auto;
30 |     height: 90rpx;
31 |     width: 90rpx;
32 |     font-family: "Lucida Console", Monaco, monospace
33 | }
34 | 
35 | .open {
36 |     background: #3BAFDA;
37 | }
38 | 
39 | .mine {
40 |     background: #E9573F;
41 | }
42 | 
43 | .flag {
44 |     
45 |     background: #F6BB42;
46 | }
47 | 
48 | .flex-item:before {
49 |     content: '';
50 |     display: block;
51 |     float: left;
52 |     padding-top:100%;
53 | }
54 | 
55 | .header {
56 |     display: flex;
57 |     font-size: 14px;
58 |     line-height: 2.5em;
59 |     text-align: center;
60 | }
61 | 
62 | .header .label {
63 |     flex: 1;
64 | }
65 | 
66 | .footer {
67 |     padding-top: 50rpx;
68 |     display: flex;   
69 |     justify-content: space-around;
70 |     align-items: center;
71 | }
72 | 
73 | .button-hover {
74 |     background-color: LightGrey;
75 | }
76 | 
77 | .tips {
78 |     margin-top: 20px;
79 |     padding: 0 0 0 20px;
80 |     font-size: 62.5%;
81 |     color: #8d8d8d;
82 | }
83 | 
--------------------------------------------------------------------------------