├── LICENSE ├── README.md └── gobang ├── .project ├── css ├── gobang.css └── gobang.less ├── img ├── bg1.jpg ├── bg2.jpg ├── bg3.jpg ├── logo.png ├── main.jpg ├── playA.png ├── playB.png └── winer.jpeg ├── index.html └── js └── game.js /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gobang_html5 2 | 五子棋,html5,canvas,js游戏,经典脑残AI算法。 3 | 如果觉得对您有帮助,请给一颗星星哟,仅表我写代码的安慰(^_^) 4 | 5 | -------------------------------------------------------------------------------- /gobang/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | gobang 4 | Create By HBuilder 5 | 6 | 7 | 8 | 9 | com.aptana.ide.core.unifiedBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.aptana.projects.webnature 16 | 17 | 18 | 19 | 1459243461761 20 | 21 | 10 22 | 23 | org.eclipse.ui.ide.orFilterMatcher 24 | 25 | 26 | org.eclipse.ui.ide.multiFilter 27 | 1.0-projectRelativePath-matches-false-false-bin 28 | 29 | 30 | org.eclipse.ui.ide.multiFilter 31 | 1.0-projectRelativePath-matches-false-false-setting 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /gobang/css/gobang.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | font-weight: normal; 6 | font-style: normal; 7 | font-family: "microsoft yahei", "arial, helvetica, sans-serif"; 8 | } 9 | .info { 10 | background-color: #F3F6F0; 11 | height: 100px; 12 | width: 600px; 13 | margin: 0 auto; 14 | border: 1px solid green; 15 | margin-bottom: 2px; 16 | visibility: hidden; 17 | box-shadow: 2px 2px 15px 0 black; 18 | } 19 | .info h1 { 20 | height: 98px; 21 | display: inline-block; 22 | width: 30px; 23 | font-size: 24px; 24 | margin: 2px 10px 0; 25 | } 26 | .desc { 27 | display: inline-block; 28 | height: 100px; 29 | width: 150px; 30 | vertical-align: top; 31 | padding-top: 1px; 32 | } 33 | .score { 34 | display: inline-block; 35 | width: 200px; 36 | vertical-align: top; 37 | } 38 | .score h1 { 39 | font-size: 30px; 40 | } 41 | .score i { 42 | font-size: 54px; 43 | display: inline-block; 44 | } 45 | .menu { 46 | display: inline-block; 47 | width: 170px; 48 | vertical-align: top; 49 | margin: 8px 0px; 50 | } 51 | .menu a { 52 | padding: 5px 6px; 53 | margin-top: 5px; 54 | display: inline-block; 55 | border: 1px solid greenyellow; 56 | background-color: green; 57 | cursor: pointer; 58 | color: white; 59 | font-size: 14px; 60 | box-shadow: 1px 1px 5px 0 black; 61 | text-decoration: none; 62 | margin-right: 8px; 63 | } 64 | .menu a:hover { 65 | background-color: #008B8B; 66 | } 67 | #pl1, 68 | #pl2 { 69 | height: 48px; 70 | vertical-align: middle; 71 | padding-left: 5px; 72 | } 73 | span { 74 | border-radius: 50%; 75 | display: inline-block; 76 | width: 30px; 77 | height: 30px; 78 | margin-top: 10px; 79 | } 80 | em { 81 | display: inline-block; 82 | height: 30px; 83 | vertical-align: 50%; 84 | margin-left: 5px; 85 | } 86 | #pl1 span { 87 | background-color: red; 88 | } 89 | #pl2 span { 90 | background-color: blue; 91 | } 92 | #pl1 img, 93 | #pl2 img { 94 | width: 30px; 95 | height: 30px; 96 | margin-top: 10px; 97 | display: inline-block; 98 | } 99 | #pl1.curr, 100 | #pl2.curr { 101 | background-color: #008B8B; 102 | } 103 | #pl1.curr em, 104 | #pl2.curr em { 105 | font-weight: bold; 106 | color: orange; 107 | } 108 | canvas, 109 | .nosupport { 110 | background-color: #f0f0f0; 111 | margin: 0 auto; 112 | display: block; 113 | cursor: pointer; 114 | /*border: 1px solid green;*/ 115 | z-index: 10; 116 | box-shadow: 2px 2px 15px 0 black; 117 | } 118 | .bb { 119 | /*overflow: hidden;*/ 120 | position: relative; 121 | } 122 | .pnl { 123 | position: absolute; 124 | width: 600px; 125 | height: 600px; 126 | z-index: 99; 127 | top: 0; 128 | left: 50%; 129 | margin-left: -300px; 130 | border-radius: 8px; 131 | box-shadow: 2px 2px 15px 0 black; 132 | background-color: #F0F0F0; 133 | } 134 | .startpnl { 135 | background: url("http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/main.jpg") no-repeat; 136 | } 137 | .winer { 138 | display: none; 139 | } 140 | .winer h1 { 141 | display: block; 142 | position: absolute; 143 | top: 15px; 144 | left: 150px; 145 | color: red; 146 | font-weight: bold; 147 | opacity: 1; 148 | } 149 | .winer input[type=button] { 150 | display: block; 151 | position: absolute; 152 | bottom: 15px; 153 | right: 15px; 154 | } 155 | .inputinfo { 156 | width: 100%; 157 | height: 520px; 158 | } 159 | h3 { 160 | display: block; 161 | font-size: 16px; 162 | color: white; 163 | border: 1px solid tomato; 164 | background-color: darkcyan; 165 | width: 100px; 166 | text-align: center; 167 | line-height: 36px; 168 | height: 40px; 169 | margin: 10px -35px; 170 | box-shadow: 1px 1px 8px 0 black; 171 | } 172 | input[type=button] { 173 | display: block; 174 | border: 1px solid greenyellow; 175 | background-color: green; 176 | cursor: pointer; 177 | width: 200px; 178 | height: 60px; 179 | margin: 0 auto; 180 | color: white; 181 | font-size: 24px; 182 | box-shadow: 2px 2px 15px 0 black; 183 | } 184 | input[type=text] { 185 | border: 1px solid #008000; 186 | outline: none; 187 | height: 18px; 188 | width: 120px; 189 | } 190 | .box { 191 | width: 500px; 192 | margin: 0 auto; 193 | } 194 | /*# sourceMappingURL=gobang.css.map */ 195 | -------------------------------------------------------------------------------- /gobang/css/gobang.less: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | font-weight: normal; 6 | font-style: normal; 7 | font-family: "microsoft yahei", "arial, helvetica, sans-serif"; 8 | } 9 | .info { 10 | background-color: #F3F6F0; 11 | height: 100px; 12 | width: 600px; 13 | margin: 0 auto; 14 | border: 1px solid green; 15 | margin-bottom: 2px; 16 | visibility: hidden; 17 | box-shadow: 2px 2px 15px 0 black; 18 | h1 { 19 | height: 98px; 20 | display: inline-block; 21 | width: 30px; 22 | font-size: 24px; 23 | margin: 2px 10px 0; 24 | } 25 | } 26 | .desc { 27 | display: inline-block; 28 | height: 100px; 29 | width: 150px; 30 | vertical-align: top; 31 | padding-top: 1px; 32 | } 33 | .score { 34 | display: inline-block; 35 | width: 200px; 36 | vertical-align: top; 37 | h1 { 38 | font-size: 30px; 39 | } 40 | i { 41 | font-size: 54px; 42 | display: inline-block; 43 | } 44 | } 45 | .menu { 46 | display: inline-block; 47 | width: 170px; 48 | vertical-align: top; 49 | margin: 8px 0px; 50 | a { 51 | padding: 5px 6px; 52 | margin-top: 5px; 53 | display: inline-block; 54 | border: 1px solid greenyellow; 55 | background-color: green; 56 | cursor: pointer; 57 | color: white; 58 | font-size: 14px; 59 | box-shadow: 1px 1px 5px 0 black; 60 | text-decoration: none; 61 | margin-right: 8px; 62 | } 63 | a:hover { 64 | background-color: #008B8B; 65 | } 66 | } 67 | #pl1, 68 | #pl2 { 69 | height: 48px; 70 | vertical-align: middle; 71 | padding-left: 5px; 72 | } 73 | span { 74 | border-radius: 50%; 75 | display: inline-block; 76 | width: 30px; 77 | height: 30px; 78 | margin-top: 10px; 79 | } 80 | em { 81 | display: inline-block; 82 | height: 30px; 83 | vertical-align: 50%; 84 | margin-left: 5px; 85 | } 86 | #pl1 span { 87 | background-color: red; 88 | } 89 | #pl2 span { 90 | background-color: blue; 91 | } 92 | #pl1 img, 93 | #pl2 img { 94 | width: 30px; 95 | height: 30px; 96 | margin-top: 10px; 97 | display: inline-block; 98 | } 99 | #pl1.curr, 100 | #pl2.curr { 101 | background-color: #008B8B; 102 | em { 103 | font-weight: bold; 104 | color: orange; 105 | } 106 | } 107 | canvas, 108 | .nosupport { 109 | background-color: #f0f0f0; 110 | margin: 0 auto; 111 | display: block; 112 | cursor: pointer; 113 | /*border: 1px solid green;*/ 114 | z-index: 10; 115 | box-shadow: 2px 2px 15px 0 black; 116 | } 117 | .bb { 118 | /*overflow: hidden;*/ 119 | position: relative; 120 | } 121 | .pnl { 122 | position: absolute; 123 | width: 600px; 124 | height: 600px; 125 | z-index: 99; 126 | top: 0; 127 | left: 50%; 128 | margin-left: -300px; 129 | border-radius: 8px; 130 | box-shadow: 2px 2px 15px 0 black; 131 | background-color: #F0F0F0; 132 | } 133 | .startpnl { 134 | background: url("http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/main.jpg") no-repeat; 135 | } 136 | .winer { 137 | display: none; // opacity: .4; 138 | h1 { 139 | display: block; 140 | position: absolute; 141 | top: 15px; 142 | left: 150px; 143 | color: red; 144 | font-weight: bold; 145 | opacity: 1; 146 | } 147 | input[type=button] { 148 | display: block; 149 | position: absolute; 150 | bottom: 15px; 151 | right: 15px; 152 | } 153 | } 154 | .inputinfo { 155 | width: 100%; 156 | height: 520px; 157 | } 158 | h3 { 159 | display: block; 160 | font-size: 16px; 161 | color: white; 162 | border: 1px solid tomato; 163 | background-color: darkcyan; 164 | width: 100px; 165 | text-align: center; 166 | line-height: 36px; 167 | height: 40px; 168 | margin: 10px -35px; 169 | box-shadow: 1px 1px 8px 0 black; 170 | } 171 | input[type=button] { 172 | display: block; 173 | border: 1px solid greenyellow; 174 | background-color: green; 175 | cursor: pointer; 176 | width: 200px; 177 | height: 60px; 178 | margin: 0 auto; 179 | color: white; 180 | font-size: 24px; 181 | box-shadow: 2px 2px 15px 0 black; 182 | } 183 | input[type=text] { 184 | border: 1px solid #008000; 185 | outline: none; 186 | height: 18px; // color: #757575; 187 | width: 120px; 188 | } 189 | .box { 190 | width: 500px; 191 | margin: 0 auto; 192 | } -------------------------------------------------------------------------------- /gobang/img/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/bg1.jpg -------------------------------------------------------------------------------- /gobang/img/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/bg2.jpg -------------------------------------------------------------------------------- /gobang/img/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/bg3.jpg -------------------------------------------------------------------------------- /gobang/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/logo.png -------------------------------------------------------------------------------- /gobang/img/main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/main.jpg -------------------------------------------------------------------------------- /gobang/img/playA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/playA.png -------------------------------------------------------------------------------- /gobang/img/playB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/playB.png -------------------------------------------------------------------------------- /gobang/img/winer.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnature/gobang_html5/7093d367180d33849d97a7eed7693535160c6226/gobang/img/winer.jpeg -------------------------------------------------------------------------------- /gobang/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 五子棋HTML5版 7 | 8 | 9 | 10 | 11 | 12 |
13 |

五子棋

14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 |
25 |

比分

26 | 0:0 27 |
28 | 33 |
34 |
35 | 40 |
41 |
42 | 43 |
44 |

选手

45 | 46 | 47 | 48 |
49 |
50 |

棋子类型

51 | 52 | 53 | 54 |
55 |
56 |

棋盘背景

57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |

线条颜色

68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 |
76 | 77 |
78 |
79 | 80 |

恭喜!xxx赢了

81 | 82 |
83 | 84 |
85 | 86 | 87 | 216 | 217 | -------------------------------------------------------------------------------- /gobang/js/game.js: -------------------------------------------------------------------------------- 1 | // ========== 2 | // =name:gobang 游戏 3 | // =anthor:jasnature 4 | // =last modify date:2016-04-13 5 | // ========== 6 | (function(win) { 7 | 8 | var gb = function(option) { 9 | 10 | var self = this, 11 | canObj = document.getElementById("cc"), 12 | can = canObj.getContext("2d"); 13 | self.contextObj = canObj; 14 | self.context = can; 15 | 16 | if (!self.context) { 17 | alert("浏览器不支持html5"); 18 | return; 19 | }; 20 | 21 | self.Opt = { 22 | lineColor: "green", 23 | chessType: 1, //1 色彩棋子 2 仿真棋子 24 | playAName: "play1", 25 | playBName: "play2", 26 | playAColor: "red", 27 | playBColor: "blue", 28 | playAImg: "img/playA.png", 29 | playBImg: "img/playB.png", 30 | backColorORImg: "default", 31 | playerBIsComputer: false 32 | }; 33 | 34 | self.operate; 35 | 36 | //合并属性 37 | for (var a in option) { 38 | //console.log(opt[a]); 39 | self.Opt[a] = option[a]; 40 | }; 41 | 42 | //私有变量 43 | var my = {}; 44 | my.enableCalcWeightNum = false; //显示AI分数 45 | my.gameover = false; 46 | //棋盘相关 47 | my.baseWidth = 30; 48 | my.lastFocusPoint = {}; //鼠标最后移动到的坐标点,计算后的 49 | my.cw = self.contextObj.offsetWidth; //棋盘宽 50 | my.ch = self.contextObj.offsetHeight; //高 51 | my.xlen = Math.ceil(my.cw / my.baseWidth); //行数 52 | my.ylen = Math.ceil(my.ch / my.baseWidth); //列 53 | my.chessRadius = 14; //棋子半径 54 | my.playerBIsComputer = false; //棋手B是否是电脑 55 | my.ComputerThinking = false; //电脑是否在下棋 56 | my.goBackC2dIsComputer = false; //最后下棋是否为电脑 57 | 58 | my.switcher = 1; //由谁下棋了 1-a 2-b or computer 59 | my.winer = -1; //赢家,值参考my.switcher 60 | my.playScoreA = 0; 61 | my.playScoreB = 0; 62 | //x,y 正方形数量(20*20) 63 | my.rectNum = my.xlen; 64 | //存储已下的点 65 | my.rectMap = []; 66 | my.NO_CHESS = -1; //没有棋子标识 67 | my.goBackC2d = {}; //最后下的数组转换坐标 68 | my.downChessmanStackC2d = []; // 记录已下棋子的顺序和位置,堆栈 69 | 70 | my.focusFlashInterval = null; //焦点闪烁线程 71 | my.focusChangeColors = ["red", "fuchsia", "#ADFF2F", "yellow", "purple", "blue"]; 72 | my.eventBinded = false; 73 | my.currChessBackImg = null; 74 | my.currChessAImg = null; 75 | my.currChessBImg = null; 76 | my.currDrawChessImg = null; 77 | my.ChessDownNum = 0; //2个玩家 下棋总数 78 | 79 | /** 80 | * 开始游戏 81 | */ 82 | self.start = function() { 83 | init(); 84 | my.gameover = false; 85 | 86 | if (self.info && typeof self.info == "function") { 87 | self.info(self.Opt); 88 | } 89 | }; 90 | 91 | /** 92 | * 重新开始游戏 93 | */ 94 | self.restart = function() { 95 | 96 | drawRect(my.goBackC2d); 97 | clearInterval(my.focusFlashInterval); 98 | 99 | //重置二维地图 100 | for (var i = 0; i < my.rectNum; i++) { 101 | for (var j = 0; j < my.rectNum; j++) { 102 | my.rectMap[i][j] = -1; 103 | } 104 | } 105 | 106 | self.Opt._map = my.rectMap; 107 | 108 | //重新初始化一些参数 109 | drawChessboard(); 110 | my.switcher = 1; //由谁下棋了 1-a 2-b 3-computer 111 | my.winer = -1; //赢家,值参考my.switcher 112 | my.gameover = false; 113 | my.ChessDownNum = 0; 114 | my.goBackC2d = {}; 115 | my.lastFocusPoint = {}; 116 | my.downChessmanStackC2d = []; 117 | 118 | self.operate(null, { 119 | player: 2, 120 | winer: my.winer, 121 | canBackTimes: 0 122 | }); 123 | }; 124 | 125 | /** 126 | * 悔棋一步 ,清棋子,并返回上一次参数 127 | */ 128 | self.back = function() { 129 | 130 | if (!my.gameover && my.downChessmanStackC2d.length > 0) { 131 | 132 | var backtime = 1; 133 | 134 | if (my.playerBIsComputer && my.switcher == 1) { 135 | backtime = 2; 136 | } 137 | 138 | for (var i = 0; i < backtime; i++) { 139 | my.goBackC2d = my.downChessmanStackC2d.pop(); 140 | 141 | if (my.goBackC2d && my.goBackC2d.I) { 142 | clearChessman(); 143 | var lastRecord = my.goBackC2d; 144 | //参数返回 145 | my.rectMap[lastRecord.I][lastRecord.J] = -1; 146 | my.winer = -1; 147 | my.switcher = lastRecord.player; 148 | 149 | if (!my.playerBIsComputer) { 150 | if (lastRecord.player == 1) { 151 | my.player = 2; 152 | } else { 153 | my.player = 1; 154 | } 155 | } 156 | 157 | self.operate(null, { 158 | player: my.player, 159 | winer: my.winer, 160 | canBackTimes: my.downChessmanStackC2d.length 161 | }); 162 | 163 | my.goBackC2d = null; 164 | } 165 | } 166 | 167 | } 168 | } 169 | 170 | /** 171 | * 初始化一些数据 172 | */ 173 | function init() { 174 | 175 | AI.init(); 176 | my.playerBIsComputer = self.Opt.playerBIsComputer; 177 | if (self.Opt.chessType == 2 && my.currChessAImg == null) { 178 | my.currChessAImg = new Image(); 179 | my.currChessAImg.src = self.Opt.playAImg; 180 | my.currChessBImg = new Image(); 181 | my.currChessBImg.src = self.Opt.playBImg; 182 | 183 | } 184 | 185 | //初始化二维地图 186 | for (var i = 0; i < my.rectNum; i++) { 187 | my.rectMap[i] = []; 188 | for (var j = 0; j < my.rectNum; j++) { 189 | 190 | my.rectMap[i][j] = -1; 191 | } 192 | 193 | } 194 | 195 | self.Opt._map = my.rectMap; 196 | 197 | //初始化canvas一些参数 198 | 199 | drawChessboard(); 200 | 201 | bindEvent(); 202 | } 203 | 204 | // self.paint = function() { 205 | // 206 | // //window.requestAnimationFrame(drawChessboard); 207 | // }; 208 | 209 | /** 210 | * 游戏逻辑 211 | */ 212 | function logic(loc, iscomputer) { 213 | my.ChessDownNum++; 214 | //计算过的参数 215 | var c2d = new Object(); 216 | if (!iscomputer) { 217 | c2d = calc2dPoint(loc); 218 | } else { 219 | //电脑给出的已经是实际坐标了,重新生成一下x,y坐标 220 | c2d.IX = loc.I * my.baseWidth; 221 | c2d.JY = loc.J * my.baseWidth; 222 | c2d.I = loc.I; 223 | c2d.J = loc.J; 224 | } 225 | 226 | var va = my.rectMap[c2d.I][c2d.J]; 227 | 228 | if (va < 0) { 229 | drawRect(my.goBackC2d); 230 | 231 | //设置二维棋子数据 232 | my.rectMap[c2d.I][c2d.J] = my.switcher; 233 | c2d.player = my.switcher; 234 | my.goBackC2d = c2d; 235 | my.downChessmanStackC2d.push(c2d); 236 | 237 | if (my.switcher === 1) { 238 | can.fillStyle = self.Opt.playAColor; 239 | can.strokeStyle = self.Opt.playAColor; 240 | my.currDrawChessImg = my.currChessAImg; 241 | my.switcher = 2; 242 | } else { 243 | can.fillStyle = self.Opt.playBColor; 244 | can.strokeStyle = self.Opt.playBColor; 245 | my.currDrawChessImg = my.currChessBImg; 246 | my.switcher = 1; 247 | } 248 | 249 | drawChessman(c2d); 250 | 251 | if (isWin(c2d)) { 252 | console.log((my.winer === 1 ? "A" : "B") + "赢了!"); 253 | c2d.winer = my.winer; 254 | if (my.winer == 1) { 255 | my.playScoreA++; 256 | } else { 257 | my.playScoreB++; 258 | } 259 | c2d.playScoreA = my.playScoreA; 260 | c2d.playScoreB = my.playScoreB; 261 | 262 | drawRect(my.goBackC2d); 263 | clearInterval(my.focusFlashInterval); 264 | } 265 | if (self.operate && typeof self.operate == "function") { 266 | c2d.canBackTimes = my.downChessmanStackC2d.length; 267 | self.operate(self.Opt, c2d); 268 | } 269 | } 270 | 271 | }; 272 | 273 | /** 274 | * 判断是否有玩家胜出 275 | * @param {Object} c2d 276 | */ 277 | function isWin(c2d) { 278 | //四个放心计数 竖 横 左斜 右斜 279 | var hcount = 0, 280 | vcount = 0, 281 | lbhcount = 0, 282 | rbhcount = 0, 283 | temp = 0; 284 | 285 | var countArray = []; 286 | 287 | //左-1 288 | for (var i = c2d.I; i >= 0; i--) { 289 | temp = my.rectMap[i][c2d.J]; 290 | if (temp < 0 || temp !== c2d.player) { 291 | break; 292 | } 293 | hcount++; 294 | countArray.push({ 295 | I: i, 296 | J: c2d.J 297 | }); 298 | } 299 | //右-1 300 | for (var i = c2d.I + 1; i < my.rectMap.length; i++) { 301 | temp = my.rectMap[i][c2d.J]; 302 | if (temp < 0 || temp !== c2d.player) { 303 | break; 304 | } 305 | hcount++; 306 | countArray.push({ 307 | I: i, 308 | J: c2d.J 309 | }); 310 | } 311 | 312 | if (countArray.length < 5) { 313 | countArray = []; 314 | //上-2 315 | for (var j = c2d.J; j >= 0; j--) { 316 | temp = my.rectMap[c2d.I][j]; 317 | if (temp < 0 || temp !== c2d.player) { 318 | break; 319 | } 320 | vcount++; 321 | 322 | countArray.push({ 323 | I: c2d.I, 324 | J: j 325 | }); 326 | } 327 | //下-2 328 | for (var j = c2d.J + 1; j < my.rectMap[c2d.I].length; j++) { 329 | temp = my.rectMap[c2d.I][j]; 330 | if (temp < 0 || temp !== c2d.player) { 331 | break; 332 | } 333 | vcount++; 334 | countArray.push({ 335 | I: c2d.I, 336 | J: j 337 | }); 338 | } 339 | } 340 | 341 | if (countArray.length < 5) { 342 | countArray = []; 343 | //左上 344 | for (var i = c2d.I, j = c2d.J; i >= 0, j >= 0; i--, j--) { 345 | if (i < 0 || j < 0) break; 346 | temp = my.rectMap[i][j]; 347 | if (temp < 0 || temp !== c2d.player) { 348 | break; 349 | } 350 | lbhcount++; 351 | countArray.push({ 352 | I: i, 353 | J: j 354 | }); 355 | } 356 | //右下 357 | if (c2d.I < my.rectMap.length - 1 && c2d.I < my.rectMap[0].length - 1) { 358 | for (var i = c2d.I + 1, j = c2d.J + 1; i < my.rectMap.length, j < my.rectMap[0].length; i++, j++) { 359 | if (i >= my.rectMap.length || j >= my.rectMap.length) break; 360 | temp = my.rectMap[i][j]; 361 | if (temp < 0 || temp !== c2d.player) { 362 | break; 363 | } 364 | lbhcount++; 365 | countArray.push({ 366 | I: i, 367 | J: j 368 | }); 369 | } 370 | } 371 | } 372 | if (countArray.length < 5) { 373 | countArray = []; 374 | //右上 375 | for (var i = c2d.I, j = c2d.J; i < my.rectMap.length, j >= 0; i++, j--) { 376 | if (i >= my.rectMap.length || j < 0) break; 377 | temp = my.rectMap[i][j]; 378 | if (temp < 0 || temp !== c2d.player) { 379 | break; 380 | } 381 | rbhcount++; 382 | countArray.push({ 383 | I: i, 384 | J: j 385 | }); 386 | } 387 | //左下 388 | if (c2d.I >= 1 && c2d.J < my.rectMap[0].length - 1) { 389 | for (var i = c2d.I - 1, j = c2d.J + 1; i > 0, j < my.rectMap[0].length; i--, j++) { 390 | if (j >= my.rectMap.length || i < 0) break; 391 | temp = my.rectMap[i][j]; 392 | if (temp < 0 || temp !== c2d.player) { 393 | break; 394 | } 395 | rbhcount++; 396 | countArray.push({ 397 | I: i, 398 | J: j 399 | }); 400 | } 401 | } 402 | } 403 | 404 | if (hcount >= 5 || vcount >= 5 || lbhcount >= 5 || rbhcount >= 5) { 405 | my.winer = c2d.player; 406 | my.gameover = true; 407 | 408 | joinWinLine(countArray); 409 | 410 | return true; 411 | } 412 | 413 | return false; 414 | } 415 | 416 | /** 417 | * 连接赢家棋子线 418 | * @param {Object} points 419 | */ 420 | function joinWinLine(points) { 421 | 422 | points.sort(function(left, right) { 423 | return (left.I + left.J) > (right.I + right.J); 424 | }); 425 | 426 | var startP = points.shift(); 427 | var endP = points.pop(); 428 | var poffset = my.baseWidth / 2; 429 | can.strokeStyle = "#FF0000"; 430 | can.lineWidth = 2; 431 | can.beginPath(); 432 | var spx = startP.I * my.baseWidth + poffset, 433 | spy = startP.J * my.baseWidth + poffset; 434 | can.arc(spx, spy, my.baseWidth / 4, 0, 2 * Math.PI, false); 435 | can.moveTo(spx, spy); 436 | var epx = endP.I * my.baseWidth + poffset, 437 | epy = endP.J * my.baseWidth + poffset; 438 | can.lineTo(epx, epy); 439 | can.moveTo(epx + my.baseWidth / 4, epy); 440 | can.arc(epx, epy, my.baseWidth / 4, 0, 2 * Math.PI, false); 441 | 442 | can.closePath(); 443 | can.stroke(); 444 | 445 | } 446 | 447 | /** 448 | * 画棋盘 449 | */ 450 | function drawChessboard() { 451 | can.save(); 452 | 453 | can.clearRect(0, 0, my.cw, my.ch); 454 | 455 | if (self.Opt.backColorORImg === "default") { 456 | self.contextObj.style.backgroundColor = "#ECC4A1"; 457 | drawline(); 458 | } else { 459 | if (my.currChessBackImg == null) { 460 | my.currChessBackImg = new Image(); 461 | my.currChessBackImg.src = self.Opt.backColorORImg; 462 | my.currChessBackImg.onload = function() { 463 | can.drawImage(my.currChessBackImg, 0, 0, 600, 600); 464 | drawline(); 465 | } 466 | } else { 467 | can.drawImage(my.currChessBackImg, 0, 0, 600, 600); 468 | drawline(); 469 | } 470 | } 471 | 472 | function drawline() { 473 | can.fillStyle = self.Opt.lineColor; 474 | can.strokeStyle = self.Opt.lineColor; 475 | can.lineWidth = 2; 476 | can.lineCap = "round"; 477 | can.lineJoin = "round"; 478 | 479 | //外壳 480 | can.strokeRect(0, 0, my.cw, my.ch); 481 | can.beginPath(); 482 | //竖线 483 | for (var i = 0; i < my.xlen; i++) { 484 | can.moveTo(i * my.baseWidth, 0); 485 | can.lineTo(i * my.baseWidth, my.ch); 486 | } 487 | //横线 488 | for (var i = 0; i < my.ylen; i++) { 489 | can.moveTo(0, i * my.baseWidth); 490 | can.lineTo(my.cw, i * my.baseWidth); 491 | } 492 | //can.fill(); 493 | can.closePath(); 494 | can.stroke(); 495 | } 496 | 497 | can.restore(); 498 | }; 499 | 500 | /** 501 | * 画棋子 502 | * @param {Object} loc 鼠标点击位置 503 | */ 504 | function drawChessman(c2d) { 505 | 506 | poffset = my.baseWidth / 2; 507 | can.save(); 508 | if (self.Opt.chessType == 1) { 509 | drawColorchess(); 510 | } else { 511 | poffset = (my.baseWidth - my.chessRadius * 2) / 2; 512 | can.drawImage(my.currDrawChessImg, c2d.IX + poffset, c2d.JY + poffset, my.chessRadius * 2, my.chessRadius * 2); 513 | } 514 | 515 | function drawColorchess() { 516 | // can.shadowOffsetX = 1; 517 | // can.shadowOffsetY = 1; 518 | // can.shadowBlur = 1; 519 | can.shadowColor = "rgba(0, 0, 0, 0.5)"; 520 | can.beginPath(); 521 | can.moveTo(c2d.IX + poffset, c2d.JY + poffset); 522 | can.arc(c2d.IX + poffset, c2d.JY + poffset, my.chessRadius - 1, 0, 2 * Math.PI, false); 523 | can.closePath(); 524 | can.fill(); 525 | } 526 | //高亮 红色 527 | drawRect(my.goBackC2d, my.focusChangeColors[0]); 528 | can.restore(); 529 | 530 | flashFocusChessman(); 531 | } 532 | 533 | function drawRect(lastRecord, defColor) { 534 | if (lastRecord) { 535 | can.strokeStyle = defColor ? defColor : self.Opt.lineColor; 536 | can.lineWidth = 2; 537 | can.strokeRect(lastRecord.IX, lastRecord.JY, my.baseWidth, my.baseWidth); 538 | } 539 | } 540 | 541 | /** 542 | * 闪烁最后下棋点 543 | */ 544 | function flashFocusChessman() { 545 | 546 | if (my.focusFlashInterval) { 547 | clearInterval(my.focusFlashInterval); 548 | } 549 | var i = 0; 550 | my.focusFlashInterval = setInterval(function() { 551 | if (i > 0 && i > my.focusChangeColors.length - 1) { 552 | i = 0; 553 | } 554 | //console.log(i); 555 | drawRect(my.goBackC2d, my.focusChangeColors[i]); 556 | i++; 557 | }, 300); 558 | } 559 | 560 | /** 561 | * 清棋子 562 | * @param {Object} c2d 563 | */ 564 | function clearChessman() { 565 | 566 | var lastRecord = my.goBackC2d; 567 | can.strokeStyle = self.Opt.lineColor; 568 | can.lineWidth = 2; 569 | can.clearRect(lastRecord.IX, lastRecord.JY, my.baseWidth, my.baseWidth); 570 | can.strokeRect(lastRecord.IX, lastRecord.JY, my.baseWidth, my.baseWidth); 571 | 572 | } 573 | 574 | /** 575 | * 坐标换算 576 | * @param {Object} loc 577 | * @return {Object} I 二维数组横点(),J二维数组纵点,IX 横点起始坐标,JY纵点起始坐标,player 最后下棋玩, winer 赢家 578 | */ 579 | function calc2dPoint(loc) { 580 | var txp = Math.floor(loc.x / my.baseWidth), 581 | typ = Math.floor(loc.y / my.baseWidth) 582 | dxp = txp * my.baseWidth, dyp = typ * my.baseWidth; 583 | 584 | loc.I = txp; 585 | loc.J = typ; 586 | loc.IX = dxp; 587 | loc.JY = dyp; 588 | 589 | return loc; 590 | } 591 | 592 | my.isChangeDraw = true; 593 | 594 | /** 595 | * 位置移动光标 596 | * @param {Object} loc 597 | */ 598 | function moveFocus(loc) { 599 | var c2d = calc2dPoint(loc); 600 | //console.log(c2d); 601 | 602 | if (my.rectMap[c2d.I][c2d.J] > 0 || 603 | (my.goBackC2d && my.goBackC2d.I == c2d.I && my.goBackC2d.J == c2d.J) 604 | ) { 605 | return; 606 | } 607 | 608 | if (my.goBackC2d && my.lastFocusPoint && my.goBackC2d.I && my.lastFocusPoint.I && my.goBackC2d.I == my.lastFocusPoint.I && my.goBackC2d.J == my.lastFocusPoint.J) { 609 | my.lastFocusPoint = c2d; 610 | return; 611 | } 612 | 613 | if (my.lastFocusPoint.I != c2d.I || my.lastFocusPoint.J != c2d.J) { 614 | drawRect(my.lastFocusPoint); 615 | my.lastFocusPoint = c2d; 616 | my.isChangeDraw = true; 617 | } 618 | if (my.isChangeDraw) { 619 | drawRect(c2d, "red"); 620 | my.isChangeDraw = false; 621 | } 622 | } 623 | 624 | /** 625 | * 绑定事件 626 | */ 627 | function bindEvent() { 628 | if (!my.eventBinded) { 629 | self.contextObj.addEventListener("touchstart", function(event) { 630 | //console.log(event); 631 | var touchObj = event.touches[0]; 632 | eventHandle({ 633 | s: "touch", 634 | x: touchObj.clientX - this.offsetLeft, 635 | y: touchObj.clientY - this.offsetTop 636 | }) 637 | }); 638 | self.contextObj.addEventListener("click", function(event) { 639 | //console.log("click event"); 640 | eventHandle({ 641 | s: "click", 642 | x: event.offsetX, 643 | y: event.offsetY 644 | }) 645 | }); 646 | 647 | self.contextObj.addEventListener("mousemove", function(event) { 648 | //console.log("mousemove event"); 649 | moveFocus({ 650 | x: event.offsetX, 651 | y: event.offsetY 652 | }); 653 | 654 | }); 655 | 656 | my.eventBinded = true; 657 | } 658 | 659 | function eventHandle(ps) { 660 | if (!my.gameover && !my.ComputerThinking) { 661 | logic(ps); 662 | 663 | if (my.playerBIsComputer && my.switcher == 2) { 664 | my.ComputerThinking = true; 665 | var pp = AI.analysis(my.goBackC2d.I, my.goBackC2d.J); 666 | logic({ 667 | I: pp.x, 668 | J: pp.y 669 | }, true); 670 | my.ComputerThinking = false; 671 | } 672 | } 673 | event.preventDefault(); 674 | event.stopPropagation(); 675 | return false; 676 | } 677 | 678 | } 679 | 680 | var AI = AI || {}; 681 | //代表每个方向 682 | AI.direction = { 683 | TOP: 1, 684 | BOTTOM: 2, 685 | LEFT: 3, 686 | RIGHT: 4, 687 | LEFT_TOP: 5, 688 | LEFT_BOTTOM: 6, 689 | RIGHT_TOP: 7, 690 | RIGHT_BOTTOM: 8 691 | }; 692 | 693 | var BOARD_SIZE = -1; //棋盘格子数量 694 | 695 | //初始化 696 | AI.init = function() { 697 | BOARD_SIZE = my.rectNum; 698 | }; 699 | 700 | /** 701 | * AI棋型分析 702 | */ 703 | AI.analysis = function(x, y) { 704 | //如果为第一步则,在玩家棋周围一格随机下棋,保证每一局棋第一步都不一样 705 | if (my.ChessDownNum == 1) { 706 | return this.getFirstPoint(x, y); 707 | } 708 | var maxX = 0, 709 | maxY = 0, 710 | maxWeight = 0, 711 | i, j, tem; 712 | 713 | for (i = BOARD_SIZE - 1; i >= 0; i--) { 714 | for (j = BOARD_SIZE - 1; j >= 0; j--) { 715 | if (my.rectMap[i][j] !== -1) { 716 | continue; 717 | } 718 | tem = this.computerWeight(i, j, 2); 719 | if (tem > maxWeight) { 720 | maxWeight = tem; 721 | maxX = i; 722 | maxY = j; 723 | 724 | } 725 | if (my.enableCalcWeightNum) { 726 | can.clearRect(i * 30 + 2, j * 30 + 2, 24, 24); 727 | can.fillText(maxWeight, i * 30 + 5, j * 30 + 15, 30); 728 | } 729 | } 730 | } 731 | return new Point(maxX, maxY); 732 | }; 733 | //下子到i,j X方向 结果: 多少连子 两边是否截断 734 | AI.putDirectX = function(i, j, chessColor) { 735 | var m, n, 736 | nums = 1, 737 | side1 = false, //两边是否被截断 738 | side2 = false; 739 | for (m = j - 1; m >= 0; m--) { 740 | if (my.rectMap[i][m] === chessColor) { 741 | nums++; 742 | } else { 743 | if (my.rectMap[i][m] === my.NO_CHESS) { 744 | side1 = true; //如果为空子,则没有截断 745 | } 746 | break; 747 | } 748 | } 749 | for (m = j + 1; m < BOARD_SIZE; m++) { 750 | if (my.rectMap[i][m] === chessColor) { 751 | nums++; 752 | } else { 753 | if (my.rectMap[i][m] === my.NO_CHESS) { 754 | side2 = true; 755 | } 756 | break; 757 | } 758 | } 759 | return { 760 | "nums": nums, 761 | "side1": side1, 762 | "side2": side2 763 | }; 764 | }; 765 | //下子到i,j Y方向 结果 766 | AI.putDirectY = function(i, j, chessColor) { 767 | var m, n, 768 | nums = 1, 769 | side1 = false, 770 | side2 = false; 771 | for (m = i - 1; m >= 0; m--) { 772 | if (my.rectMap[m][j] === chessColor) { 773 | nums++; 774 | } else { 775 | if (my.rectMap[m][j] === my.NO_CHESS) { 776 | side1 = true; 777 | } 778 | break; 779 | } 780 | } 781 | for (m = i + 1; m < BOARD_SIZE; m++) { 782 | if (my.rectMap[m][j] === chessColor) { 783 | nums++; 784 | } else { 785 | if (my.rectMap[m][j] === my.NO_CHESS) { 786 | side2 = true; 787 | } 788 | break; 789 | } 790 | } 791 | return { 792 | "nums": nums, 793 | "side1": side1, 794 | "side2": side2 795 | }; 796 | }; 797 | //下子到i,j XY方向 结果 798 | AI.putDirectXY = function(i, j, chessColor) { 799 | var m, n, 800 | nums = 1, 801 | side1 = false, 802 | side2 = false; 803 | for (m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) { 804 | if (my.rectMap[m][n] === chessColor) { 805 | nums++; 806 | } else { 807 | if (my.rectMap[m][n] === my.NO_CHESS) { 808 | side1 = true; 809 | } 810 | break; 811 | } 812 | } 813 | for (m = i + 1, n = j + 1; m < BOARD_SIZE && n < BOARD_SIZE; m++, n++) { 814 | if (my.rectMap[m][n] === chessColor) { 815 | nums++; 816 | } else { 817 | if (my.rectMap[m][n] === my.NO_CHESS) { 818 | side2 = true; 819 | } 820 | break; 821 | } 822 | } 823 | return { 824 | "nums": nums, 825 | "side1": side1, 826 | "side2": side2 827 | }; 828 | }; 829 | AI.putDirectYX = function(i, j, chessColor) { 830 | var m, n, 831 | nums = 1, 832 | side1 = false, 833 | side2 = false; 834 | for (m = i - 1, n = j + 1; m >= 0 && n < BOARD_SIZE; m--, n++) { 835 | if (my.rectMap[m][n] === chessColor) { 836 | nums++; 837 | } else { 838 | if (my.rectMap[m][n] === my.NO_CHESS) { 839 | side1 = true; 840 | } 841 | break; 842 | } 843 | } 844 | for (m = i + 1, n = j - 1; m < BOARD_SIZE && n >= 0; m++, n--) { 845 | if (my.rectMap[m][n] === chessColor) { 846 | nums++; 847 | } else { 848 | if (my.rectMap[m][n] === my.NO_CHESS) { 849 | side2 = true; 850 | } 851 | break; 852 | } 853 | } 854 | return { 855 | "nums": nums, 856 | "side1": side1, 857 | "side2": side2 858 | }; 859 | }; 860 | 861 | /** 862 | * 计算AI下棋权重 863 | * chessColor 玩家1为玩家2为AI 864 | */ 865 | AI.computerWeight = function(i, j, chessColor) { 866 | //基于棋盘位置权重(越靠近棋盘中心权重越大) 867 | var weight = 19 - (Math.abs(i - 19 / 2) + Math.abs(j - 19 / 2)), 868 | pointInfo = {}; //某点下子后连子信息 869 | 870 | //x方向 871 | pointInfo = this.putDirectX(i, j, chessColor); 872 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重 873 | pointInfo = this.putDirectX(i, j, chessColor - 1); 874 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重 875 | //y方向 876 | pointInfo = this.putDirectY(i, j, chessColor); 877 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重 878 | pointInfo = this.putDirectY(i, j, chessColor - 1); 879 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重 880 | //左斜方向 881 | pointInfo = this.putDirectXY(i, j, chessColor); 882 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重 883 | pointInfo = this.putDirectXY(i, j, chessColor - 1); 884 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重 885 | //右斜方向 886 | pointInfo = this.putDirectYX(i, j, chessColor); 887 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重 888 | pointInfo = this.putDirectYX(i, j, chessColor - 1); 889 | weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重 890 | return weight; 891 | }; 892 | //权重方案 活:两边为空可下子,死:一边为空 893 | //其实还有很多种方案,这种是最简单的 894 | AI.weightStatus = function(nums, side1, side2, isAI) { 895 | var weight = 0; 896 | switch (nums) { 897 | case 1: 898 | if (side1 && side2) { 899 | weight = isAI ? 15 : 10; //一 900 | } 901 | break; 902 | case 2: 903 | if (side1 && side2) { 904 | weight = isAI ? 100 : 50; //活二 905 | } else if (side1 || side2) { 906 | weight = isAI ? 10 : 5; //死二 907 | } 908 | break; 909 | case 3: 910 | if (side1 && side2) { 911 | weight = isAI ? 500 : 200; //活三 912 | } else if (side1 || side2) { 913 | weight = isAI ? 30 : 20; //死三 914 | } 915 | break; 916 | case 4: 917 | if (side1 && side2) { 918 | weight = isAI ? 5000 : 2000; //活四 919 | } else if (side1 || side2) { 920 | weight = isAI ? 400 : 100; //死四 921 | } 922 | break; 923 | case 5: 924 | weight = isAI ? 100000 : 10000; //五 925 | break; 926 | default: 927 | weight = isAI ? 500000 : 250000; 928 | break; 929 | } 930 | return weight; 931 | }; 932 | 933 | //AI第一步棋 934 | //参数x,y为玩家第一步棋的坐标 935 | AI.getFirstPoint = function(x, y) { 936 | var point = new Point(x, y); 937 | if (x < 3 || x > BOARD_SIZE - 3 || y < 3 || y > BOARD_SIZE - 3) { 938 | point.x = BOARD_SIZE >> 1; 939 | point.y = BOARD_SIZE >> 1; 940 | } else { 941 | 942 | var direction = Math.ceil(Math.random() * 8); 943 | if (direction <= 0 || direction > 8) { 944 | direction = 2; 945 | } 946 | switch (direction) { 947 | case this.direction.TOP: 948 | point.y = y - 1; 949 | break; 950 | case this.direction.BOTTOM: 951 | point.y = y + 1; 952 | break; 953 | case this.direction.LEFT: 954 | point.x = x - 1; 955 | break; 956 | case this.direction.RIGHT: 957 | point.x = x + 1; 958 | break; 959 | case this.direction.LEFT_TOP: 960 | point.x = x - 1; 961 | point.y = y - 1; 962 | break; 963 | case this.direction.LEFT_BOTTOM: 964 | point.x = x - 1; 965 | point.y = y + 1; 966 | break; 967 | case this.direction.RIGHT_TOP: 968 | point.x = x + 1; 969 | point.y = y - 1; 970 | break; 971 | case this.direction.RIGHT_BOTTOM: 972 | point.x = x + 1; 973 | point.y = y + 1; 974 | break; 975 | default: 976 | point.x = x - 1; 977 | point.y = y - 1; 978 | break; 979 | } 980 | } 981 | return point; 982 | }; 983 | 984 | function Point(x, y) { 985 | var self = this; 986 | self.x = x; 987 | self.y = y; 988 | }; 989 | 990 | }; 991 | 992 | win.gobang = gb; 993 | 994 | })(window); 995 | --------------------------------------------------------------------------------