diff --git a/LICENSE b/LICENSE index 733c072..799d59b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,675 +1 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - +Copyleft by Misakamm diff --git a/dllai/ai.cpp b/dllai/ai.cpp new file mode 100644 index 0000000..40f230f --- /dev/null +++ b/dllai/ai.cpp @@ -0,0 +1,168 @@ +#include "ai.h" + +namespace AI { + int Evaluate(const GameField& field, int att, int clear, int depth, int player) { + int score = 0; + int field_w = field.width(), field_h = field.height(); + int min_y[32] = {0}; + struct factor { + int hole, h_change, y_factor, h_variance, att, noattclear; + } ai_factor_l[3] = { + {-50, -5, -10, -10, 40, -30}, + {-50, -5, -10, -10, 30, -35}, + {-50, -5, -10, -10, 20, -35}, + }; + factor ai_factor; + if ( depth > 2 ) ai_factor = ai_factor_l[2]; + else ai_factor = ai_factor_l[depth]; + + // get all columns height + { + int beg_y = 0; + while ( field.row[beg_y] == 0 ) ++beg_y; + for ( int x = 0; x < field_w; ++x) { + for ( int y = beg_y, ey = field_h + 1; y <= ey; ++y) { + if ( field.row[y] & ( 1 << x ) ) { + min_y[x] = y; + break; + } + } + } + } + min_y[field_w] = min_y[field_w-2]; + // find holes + { + int hole_score = 0; + for ( int x = 0; x < field_w; ++x) { + for ( int y = min_y[x] + 1; y <= field_h; ++y) { + if ( ( field.row[y] & ( 1 << x ) ) == 0) { + hole_score += ai_factor.hole; + } + } + } + score += hole_score; + } + // height change + { + int last = min_y[1]; + for ( int x = 0; x <= field_w; last = min_y[x], ++x) { + int v = min_y[x] - last; + int absv = abs(v); + score += absv * ai_factor.h_change; + } + } + // variance + { + int h_variance_score = 0; + int avg = 0; + { + int sum = 0; + int sample_cnt = 0; + for ( int x = 0; x < field_w; ++x) { + avg += min_y[x]; + } + { + double h = field_h - (double)avg / field_w; + score += int(ai_factor.y_factor * h * h / field_h); + } + for ( int x = 0; x < field_w; ++x) { + int t = avg - min_y[x] * field_w; + if ( abs(t) > field_h * field_w / 4 ) { + if ( t > 0 ) t = field_h * field_w / 4; + else t = -(field_h * field_w / 4); + } + sum += t * t; + ++sample_cnt; + } + if ( sample_cnt > 0 ) { + h_variance_score = sum * ai_factor.h_variance / (sample_cnt * 100); + } + score += h_variance_score; + } + } + // clear and attack + if ( clear > 0 ) + { + if ( depth > 0 ) { + score += int( att * ai_factor.att * ((double)att / clear - 0.7) ); + } else { + score += att * ai_factor.att; + } + if ( att == 0 ) { + score += clear * ai_factor.noattclear; + } + } + return score; + } + int pasteClearAttack(GameField& field, int gemnum, int x, int y, int spin, signed char wallkick_spin, int &att) { + wallkick_spin = field.WallKickValue(gemnum, x, y, spin, wallkick_spin); + field.paste(x, y, getGem(gemnum, spin)); + int clear = field.clearLines( wallkick_spin ); + att = field.getAttack( clear, wallkick_spin ); + return clear; + } + MovingSimple AISearch(const GameField& field, Gem cur, bool curCanHold, int x, int y, const std::vector& next + , bool canhold, int upcomeAtt, int level, int player + , int maxdepth, int* pscore, int lastatt, int lastclear, int depth) { + + std::vector movs; + if ( pscore ) *pscore = SCORE_LOSE; + GenMoving(field, movs, cur, x, y, false); + if ( movs.empty() ) { + return MovingSimple(); + } + if ( maxdepth > level ) maxdepth = level; + + MovingSimple best; + int maxScore = SCORE_LOSE; + for (size_t i = 0; i < movs.size() ; ++i) { + GameField newfield = field; + int clear, att; + clear = pasteClearAttack(newfield, cur.num, movs[i].x, movs[i].y, movs[i].spin, movs[i].wallkick_spin, att); + int score = 0; + if ( maxdepth > 0 && next.size() > 0) { + std::vector newnext( next.begin() + 1, next.end() ); + AISearch(newfield, next[0], canhold, gem_beg_x, gem_beg_y, newnext, canhold, upcomeAtt, level, player, maxdepth - 1, &score, lastatt + att, lastclear + clear, depth + 1); + } else { + score += Evaluate(newfield, lastatt + att, lastclear + clear, depth, player); + } + if ( score > maxScore ) { + maxScore = score; + best = movs[i]; + } + } + int hold_num = field.m_hold; + if ( field.m_hold == 0 && ! next.empty()) { + hold_num = next[0].num; + } + if ( canhold && curCanHold && hold_num ) { + GenMoving(field, movs, AI::getGem(hold_num, 0), gem_beg_x, gem_beg_y, true); + if ( ! movs.empty() ) { + for (size_t i = 0; i < movs.size() ; ++i) { + GameField newfield = field; + int clear, att; + clear = pasteClearAttack(newfield, hold_num, movs[i].x, movs[i].y, movs[i].spin, movs[i].wallkick_spin, att); + int score = 0; + newfield.m_hold = cur.num; + if ( maxdepth > 0 && (field.m_hold != 0 && next.size() > 0 || next.size() > 1) ) { + if ( field.m_hold == 0 ) { + std::vector newnext( next.begin() + 2, next.end() ); + AISearch(newfield, next[1], canhold, gem_beg_x, gem_beg_y, newnext, canhold, upcomeAtt, level, player, maxdepth - 1, &score, lastatt + att, lastclear + clear, depth + 1); + } else { + std::vector newnext( next.begin() + 1, next.end() ); + AISearch(newfield, next[0], canhold, gem_beg_x, gem_beg_y, newnext, canhold, upcomeAtt, level, player, maxdepth - 1, &score, lastatt + att, lastclear + clear, depth + 1); + } + } else { + score += Evaluate(newfield, lastatt + att, lastclear + clear, depth, player); + } + if ( score > maxScore ) { + maxScore = score; + best = movs[i]; + } + } + } + } + if ( pscore ) *pscore = maxScore; + return best; + } +} diff --git a/dllai/ai.h b/dllai/ai.h new file mode 100644 index 0000000..9b36f24 --- /dev/null +++ b/dllai/ai.h @@ -0,0 +1,158 @@ +#pragma once + +#include "gamefield.h" +#include + +#define AI_MAX_DEPTH 2 + +namespace AI { + struct Moving + { + enum { + MOV_NULL, + MOV_L, + MOV_R, + MOV_LL, + MOV_RR, + MOV_D, + MOV_DD, + MOV_LSPIN, + MOV_RSPIN, + MOV_DROP, + MOV_HOLD, + MOV_SPIN2, + }; + std::vector movs; + int x, y; + int score; + signed char spin; + signed char wallkick_spin; + Moving () { wallkick_spin = 0; movs.reserve (16); } + Moving ( const Moving & m ) { + movs = m.movs; + x = m.x; + y = m.y; + spin = m.spin; + score = m.score; + wallkick_spin = m.wallkick_spin; + } + Moving ( const Moving & m, int _spin ) { + movs.reserve (16); + movs = m.movs; + spin = (signed char)_spin; + } + bool operator < (const Moving& _m) const { + return score > _m.score; + } + bool operator == (const Moving& _m) const { + if ( x != _m.x || y != _m.y ) return false; + if ( spin != _m.spin ) return false; + if ( wallkick_spin != _m.wallkick_spin ) return false; + return true; + } + }; + struct MovingSimple + { + enum { + MOV_NULL, + MOV_L, + MOV_R, + MOV_LL, + MOV_RR, + MOV_D, + MOV_DD, + MOV_LSPIN, + MOV_RSPIN, + MOV_DROP, + MOV_HOLD, + MOV_SPIN2, + }; + enum { + INVALID_POS = -64, + }; + int x, y; + int lastmove; + signed char spin; + signed char wallkick_spin; + bool hold; + MovingSimple () { x = INVALID_POS; wallkick_spin = 0; lastmove = MovingSimple::MOV_NULL; } + MovingSimple ( const Moving & m ) { + x = m.x; + y = m.y; + spin = m.spin; + wallkick_spin = m.wallkick_spin; + if (m.movs.empty()) hold = false; + else hold = (m.movs[0] == MovingSimple::MOV_HOLD); + if (m.movs.empty()) lastmove = MovingSimple::MOV_NULL; + else lastmove = m.movs.back(); + } + MovingSimple ( const MovingSimple & m ) { + x = m.x; + y = m.y; + spin = m.spin; + wallkick_spin = m.wallkick_spin; + hold = m.hold; + lastmove = m.lastmove; + } + MovingSimple ( const MovingSimple & m, int _spin ) { + lastmove = m.lastmove; + hold = m.hold; + spin = (signed char)_spin; + } + bool operator == (const MovingSimple& _m) const { + if ( x != _m.x || y != _m.y ) return false; + if ( spin != _m.spin ) return false; + if ( lastmove != _m.lastmove ) return false; + if ( hold != _m.hold ) return false; + if ( wallkick_spin != _m.wallkick_spin ) return false; + return true; + } + }; + template + struct MovList + { + std::vector queue; + //T queue[1024]; + int beg, end; + MovList() { + beg = end = 0; + } + MovList( size_t size ) { + beg = end = 0; + //queue.resize( size ); + queue.reserve( size ); + } + void clear() { + beg = end = 0; + queue.clear(); + } + size_t size() const { + return end - beg; + } + T& back() { + return queue[end-1]; + } + void push(const T& mov) { + queue.push_back(mov); + //queue[end] = mov; + ++end; + } + bool empty () const { + return beg == end; + } + void pop(T& mov) { + mov = queue[beg++]; + } + }; + enum AIScore { + SCORE_LOSE = -100000000, + }; + void setComboList( std::vector combolist ); + int getComboAttack( int combo ); + void GenMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold); + void FindPathMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold); + MovingSimple AISearch(const GameField& field, Gem cur, bool curCanHold, int x, int y, const std::vector& next + , bool canHold, int upcomeAtt, int level, int player + , int maxdepth = AI_MAX_DEPTH, int* score = NULL, int lastatt = 0, int lastclear = 0, int depth = 0); + void setSpin180( bool enable ); +} diff --git a/dllai/dllai.cpp b/dllai/dllai.cpp new file mode 100644 index 0000000..fe53809 --- /dev/null +++ b/dllai/dllai.cpp @@ -0,0 +1,155 @@ +// dllai.cpp : Defines the exported functions for the DLL application. +// + +#define _CRT_SECURE_NO_WARNINGS +#include "ai.h" +#include +#include +#include +#include +#include "windows.h" + +#define DLLEXPORT extern "C" __declspec(dllexport) + +char g_result[8][1024]; +AI::MovingSimple last_best[8]; + +char* result(int player, std::string r) +{ + return strcpy( g_result[player], r.c_str() ); +} + +// very important value +#define AI_DLL_VERSION 2 + +DLLEXPORT +int AIDllVersion() +{ + return AI_DLL_VERSION; +} + +DLLEXPORT +char* AIName( int level ) { + char name[200]; + if ( level > AI_MAX_DEPTH ) level = AI_MAX_DEPTH; + sprintf( name, "LV%d", level); + return result( 0, std::string("Baka(9) ") + name ); +} + +/* +all 'char' type is using the characters in ' ITLJZSO' + +field data like this: +00........ -> 0x3 +00.0...... -> 0xb +00000..... -> 0x1f + +b2b: the count of special attack, the first one set b2b=1, but no extra attack. Have extra attacks when b2b>=2 +combo: first clear set combo=1, so the comboTable in toj rule is [0, 0, 0, 1, 1, 2, 2, 3, ...] +next: array size is 'maxDepth' +x, y, spin: the active piece's x/y/orientation, + x/y is the up-left corner's position of the active piece. + see tetris_gem.cpp for the bitmaps. +curCanHold: indicates whether you can use hold on current move. + might be caused by re-think after a hold move. +canhold: false if hold is completely disabled. +comboTable: -1 is the end of the table. +*/ +DLLEXPORT +char* TetrisAI(int overfield[], int field[], int field_w, int field_h, int b2b, int combo, + char next[], char hold, bool curCanHold, char active, int x, int y, int spin, + bool canhold, bool can180spin, int upcomeAtt, int comboTable[], int maxDepth, int level, int player) +{ + AI::GameField gamefield(field_w, field_h); // always 10 x 22 + std::vector gemNext; + std::map gemMap; + // init + gemMap[' '] = AI::GEMTYPE_NULL; + gemMap['I'] = AI::GEMTYPE_I; + gemMap['T'] = AI::GEMTYPE_T; + gemMap['L'] = AI::GEMTYPE_L; + gemMap['J'] = AI::GEMTYPE_J; + gemMap['Z'] = AI::GEMTYPE_Z; + gemMap['S'] = AI::GEMTYPE_S; + gemMap['O'] = AI::GEMTYPE_O; + // gamefield + for ( int iy = 0; iy <= field_h; ++iy ) { + gamefield.row[iy] = field[iy]; + } + for ( int iy = 0; iy < 8; ++iy ) { + gamefield.row[-iy-1] = overfield[iy]; + } + gamefield.m_hold = gemMap[hold]; + gamefield.b2b = b2b; + gamefield.combo = combo; + // next + for ( int i = 0; i < maxDepth; ++i) { + gemNext.push_back( AI::getGem(gemMap[next[i]], 0) ); + } + // combo list + for ( int i = 0; i < 100; ++i) { + if ( comboTable[i] < 0 ) { + AI::setComboList( std::vector(comboTable, comboTable + i - 1) ); + break; + } + } + // rule + AI::setSpin180(can180spin); + + if ( level > AI_MAX_DEPTH ) level = AI_MAX_DEPTH; + + // player is the player number, for randomize or something else, to make different player do the different thing if it is necessary + AI::MovingSimple best = AI::AISearch(gamefield, AI::getGem(gemMap[active], spin), curCanHold, x, y, gemNext, canhold, upcomeAtt, level, player); + + // find path + AI::Moving mov; + if ( best.x != AI::MovingSimple::INVALID_POS ) { + int hold_num = gamefield.m_hold; + if ( gamefield.m_hold == 0 && ! gemNext.empty()) { + hold_num = gemNext[0].num; + } + std::vector movs; + AI::Gem cur; + if ( best.hold ) { + cur = AI::getGem(hold_num, 0); + FindPathMoving(gamefield, movs, cur, AI::gem_beg_x, AI::gem_beg_y, true); + } else { + cur = AI::getGem(gemMap[active], spin); + FindPathMoving(gamefield, movs, cur, x, y, false); + } + for ( size_t i = 0; i < movs.size(); ++i ) { + if ( movs[i].x == best.x && movs[i].y == best.y && movs[i].spin == best.spin ) { + if ( cur.num == AI::GEMTYPE_T && movs[i].wallkick_spin >= best.wallkick_spin || cur.num != AI::GEMTYPE_T ) { + mov = movs[i]; + } + } + } + } + if ( mov.movs.empty() ) { + mov.movs.clear(); + mov.movs.push_back( AI::Moving::MOV_NULL ); + mov.movs.push_back( AI::Moving::MOV_DROP ); + } else { + last_best[player] = best; + } + + // return + std::map outMap; + outMap[AI::Moving::MOV_NULL] = ' '; + outMap[AI::Moving::MOV_L] = 'l'; + outMap[AI::Moving::MOV_R] = 'r'; + outMap[AI::Moving::MOV_LL] = 'L'; + outMap[AI::Moving::MOV_RR] = 'R'; + outMap[AI::Moving::MOV_D] = 'd'; + outMap[AI::Moving::MOV_DD] = 'D'; + outMap[AI::Moving::MOV_LSPIN] = 'z'; + outMap[AI::Moving::MOV_SPIN2] = 'x'; + outMap[AI::Moving::MOV_RSPIN] = 'c'; + outMap[AI::Moving::MOV_HOLD] = 'v'; + outMap[AI::Moving::MOV_DROP] = 'V'; + std::string out; + for ( size_t i = 0; i < mov.movs.size(); ++i ) { + out.push_back(outMap[mov.movs[i]]); + } + return result(player, out); +} diff --git a/dllai/dllai.vcproj b/dllai/dllai.vcproj new file mode 100644 index 0000000..2a0012e --- /dev/null +++ b/dllai/dllai.vcproj @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dllai/dllai.vcxproj b/dllai/dllai.vcxproj new file mode 100644 index 0000000..1010768 --- /dev/null +++ b/dllai/dllai.vcxproj @@ -0,0 +1,143 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + XP_Release + Win32 + + + + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3} + Win32Proj + dllai + + + + DynamicLibrary + true + v110 + Unicode + + + DynamicLibrary + false + v110 + true + Unicode + + + DynamicLibrary + false + v110_xp + true + Unicode + + + + + + + + + + + + + + + + true + $(SolutionDir)bin\ + + + false + $(SolutionDir)bin\ + + + false + $(SolutionDir)bin\ + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;DLLAI_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;DLLAI_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + Level4 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;DLLAI_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + true + true + true + + + + + + + + + + + + + + + + false + + + false + false + + + + + + + + + + + + \ No newline at end of file diff --git a/dllai/dllmain.cpp b/dllai/dllmain.cpp new file mode 100644 index 0000000..5aaee7c --- /dev/null +++ b/dllai/dllmain.cpp @@ -0,0 +1,37 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "windows.h" +extern "C" { +HMODULE g_hModule; +} + +#ifdef _DEBUG +#include +#define SHOW_CONSOLE +#endif + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + g_hModule = hModule; +#ifdef SHOW_CONSOLE + { + AllocConsole(); + freopen("CONOUT$","w+t",stdout); + freopen("CONIN$","r+t",stdin); + printf("Debug Begin. Using 'printf' to output debug infomation"); + } +#endif + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/dllai/gamefield.h b/dllai/gamefield.h new file mode 100644 index 0000000..013cb53 --- /dev/null +++ b/dllai/gamefield.h @@ -0,0 +1,300 @@ +#pragma once +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + +#include +#include "tetris_gem.h" +#define AI_POOL_MAX_H 50 + +namespace AI { + int getComboAttack( int combo ); + void setAllSpin(bool allSpin); + bool isEnableAllSpin(); + + const int gem_add_y = 20; + const int gem_beg_x = 3; + const int gem_beg_y = 1; + + struct GameField { + signed char m_w, m_h; + signed short combo; + signed short b2b; + unsigned long m_w_mask; + unsigned long m_row[AI_POOL_MAX_H]; + int m_hold; + unsigned long *row; + GameField () { + row = &m_row[gem_add_y]; + } + GameField ( const GameField& field ) { + row = &m_row[gem_add_y]; + *this = field; + } + GameField (int w, int h) { + row = &m_row[gem_add_y]; + reset((signed char)w, (signed char)h); + } + int width() const { return m_w; } + int height() const { return m_h; } + void reset (int w, int h) { + m_w = (signed char)w; + m_h = (signed char)h; + combo = 0; + b2b = 0; + m_hold = 0; + m_w_mask = ( 1 << w ) - 1; + for (int i = 0; i < AI_POOL_MAX_H; ++i) { + m_row[i] = 0; + } + for (int i = gem_add_y + m_h + 1; i < AI_POOL_MAX_H; ++i) { + m_row[i] = (unsigned)-1; + } + } + GameField& operator = (const GameField& field) { + memcpy( this, &field, (size_t)&row - (size_t)this ); + return *this; + } + bool operator == (const GameField& field) const { + if ( m_w != field.m_w || m_h != field.m_h ) return false; + for ( int i = 0; i <= m_h + gem_add_y; ++i ) { + if ( m_row[i] != field.m_row[i] ) return false; + } + if ( m_hold != field.m_hold ) return false; + if ( combo != field.combo ) return false; + if ( b2b != field.b2b ) return false; + return true; + } + inline bool isCollide(int y, const Gem & gem) const { + for ( int h = 3; h >= 0; --h ) { + if ( y + h > m_h && gem.bitmap[h] ) return true; + if ( row[y + h] & gem.bitmap[h] ) return true; + } + return false; + } + inline bool isCollide(int x, int y, const Gem & gem) const { + Gem _gem = gem; + for ( int h = 0; h < 4; ++h ) { + if ( x < 0 ) { + if (gem.bitmap[h] & ( ( 1 << (-x) ) - 1 ) ) return true; + _gem.bitmap[h] >>= -x; + } else { + if ( (gem.bitmap[h] << x) & ~m_w_mask ) return true; + _gem.bitmap[h] <<= x; + } + if ( y + h > m_h && _gem.bitmap[h] ) return true; + if ( row[y + h] & _gem.bitmap[h] ) return true; + } + return false; + } + bool wallkickTest(int& x, int& y, const Gem & gem, int spinclockwise) const { + static int Iwallkickdata[4][2][4][2] = { + { // O + { // R + { 2, 0},{-1, 0},{ 2,-1},{-1, 2}, + }, + { // L + { 1, 0},{-2, 0},{ 1, 2},{-2,-1}, + }, + }, + { // L + { // O + {-1, 0},{ 2, 0},{-1,-2},{ 2, 1}, + }, + { // 2 + { 2, 0},{-1, 0},{ 2,-1},{-1, 2}, + }, + }, + { // 2 + { // L + {-2, 0},{ 1, 0},{-2, 1},{ 1,-2}, + }, + { // R + {-1, 0},{ 2, 0},{-1,-2},{ 2, 1}, + }, + }, + { // R + { // 2 + { 1, 0},{-2, 0},{ 1, 2},{-2,-1}, + }, + { // O + {-2, 0},{ 1, 0},{-2, 1},{ 1,-2}, + }, + }, + }; + static int wallkickdata[4][2][4][2] = { + { // O + { // R + { 1, 0},{ 1, 1},{ 0,-2},{ 1,-2}, + }, + { // L + {-1, 0},{-1, 1},{ 0,-2},{-1,-2}, + }, + }, + { // L + { // O + { 1, 0},{ 1,-1},{ 0, 2},{ 1, 2}, + }, + { // 2 + { 1, 0},{ 1,-1},{ 0, 2},{ 1, 2}, + }, + }, + { // 2 + { // L + {-1, 0},{-1, 1},{ 0,-2},{-1,-2}, + }, + { // R + { 1, 0},{ 1, 1},{ 0,-2},{ 1,-2}, + }, + }, + { // R + { // 2 + {-1, 0},{-1,-1},{ 0, 2},{-1, 2}, + }, + { // O + {-1, 0},{-1,-1},{ 0, 2},{-1, 2}, + }, + }, + }; + int (*pdata)[2][4][2] = wallkickdata; + if ( gem.num == GEMTYPE_I ) pdata = Iwallkickdata; + for ( int itest = 0; itest < 4; ++itest) { + int dx = x + pdata[gem.spin][spinclockwise][itest][0]; + int dy = y + pdata[gem.spin][spinclockwise][itest][1]; + if ( ! isCollide(dx, dy, gem) ) { + x = dx; y = dy; + return true; + } + } + return false; + } + void paste(int x, int y, const Gem & gem) { + for ( int h = 0; h < gem.geth(); ++h ) { + if (x >= 0) + row[y + h] |= gem.bitmap[h] << x; + else + row[y + h] |= gem.bitmap[h] >> (-x); + } + } + signed char isWallKickSpin(int x, int y, const Gem & gem) const { + if ( isEnableAllSpin() ) { + if ( isCollide( x - 1, y, gem ) + && isCollide( x + 1, y, gem ) + && isCollide( x, y - 1, gem )) { + return 1; + } + } else { + if ( gem.num == GEMTYPE_T ) { //T + int cnt = 0; + if ( x < 0 || (row[y] & (1 << x))) ++cnt; + if ( x < 0 || y+2 > m_h || (row[y+2] & (1 << x))) ++cnt; + if ( x+2 >= m_w || (row[y] & (1 << (x+2)))) ++cnt; + if ( x+2 >= m_w || y+2 > m_h || (row[y+2] & (1 << (x+2)))) ++cnt; + if ( cnt >= 3 ) return 1; + } + } + return 0; + } + signed char WallKickValue(int gem_num, int x, int y, int spin, signed char wallkick_spin) const { + if ( ! isWallKickSpin( x, y, getGem(gem_num, spin) ) ) { + return wallkick_spin = 0; + } + if ( isEnableAllSpin() ) { + if ( wallkick_spin == 2) { + wallkick_spin = 1; + Gem g = getGem(gem_num, spin); + for ( int dy = 0; dy < 4; ++dy ) { //KOS mini test + if ( g.bitmap[dy] == 0 ) continue; + if ( ((g.bitmap[dy] << x) | row[y+dy]) == m_w_mask ) continue; + wallkick_spin = 2; + break; + } + } + } else { + if ( wallkick_spin == 2 ) { + if ( ! isCollide( x, y, getGem(gem_num, spin^2) ) ) { + wallkick_spin = 1; // not t-mini + } + } + } + return wallkick_spin; + } + int clearLines( signed char _wallkick_spin ) { + int clearnum = 0; + int h2 = m_h; + for (int h = m_h; h >= -gem_add_y; --h) { + if ( row[h] != m_w_mask) { + row[h2--] = row[h]; + } else { + ++ clearnum; + } + } + for (int h = h2; h >= -gem_add_y; --h) { + row[h] = 0; + } + if ( clearnum > 0 ) { + ++combo; + if ( clearnum == 4 ) { + ++b2b; + } else if ( _wallkick_spin > 0 ) { + ++b2b; + } else { + b2b = 0; + } + } else { + combo = 0; + } + return clearnum; + } + int getAttack( int clearfull, signed char wallkick ) { // wallkick: 0: no spin 1: spin only 2: wallkick + int attack = 0; + if ( clearfull > 1 ) { + if ( clearfull < 4 ) { + attack = clearfull - 1; + } else { + attack = clearfull; + if ( b2b > 1 ) attack += 1; + } + } + if ( clearfull > 0 ) { + if ( wallkick ) { + if ( isEnableAllSpin() ) { + attack += clearfull + 1; + if ( wallkick == 2 ) { // mini + attack -= 1; // mini minus + } + } else { + if ( b2b > 1 ) attack += 1; + if ( clearfull == 1 ) { + if ( wallkick == 2 ) { // mini + attack += 1; + } else { + attack += 2; + } + } else { + attack += clearfull + 1; + } + if ( clearfull == 3 ) { + if ( b2b > 1 ) attack += 1; + } + } + } + attack += getComboAttack( combo ); + { + int i = gem_add_y + m_h; + for ( ; i >= 0; --i ) { + if ( m_row[i] ) break; + } + if ( i < 0 ) { + attack += 6; + } + } + } + return attack; + } + void addRow( int rowdata ) { + for ( int h = -gem_add_y + 1; h <= m_h; ++h ) { + row[h-1] = row[h]; + } + row[m_h] = rowdata; + } + }; +} diff --git a/dllai/genmove.cpp b/dllai/genmove.cpp new file mode 100644 index 0000000..8028768 --- /dev/null +++ b/dllai/genmove.cpp @@ -0,0 +1,488 @@ +#include "ai.h" +#define USING_MOV_D 0 +#define GENMOV_W_MASK 15 +#define SWITCH_USING_HEIGHT_OPT + +#define _MACRO_CREATE_MOVINGSIMPLE(arg_action_name,arg_wkspin) \ + MovingSimple nm = m; \ + nm.x = nx; \ + nm.y = ny; \ + nm.spin = ns; \ + nm.lastmove = Moving::##arg_action_name##; \ + nm.wallkick_spin = arg_wkspin + +#define _MACRO_CREATE_MOVING(arg_action_name,arg_wkspin) \ + Moving nm = m; \ + nm.x = nx; \ + nm.y = ny; \ + nm.spin = ns; \ + nm.movs.push_back(Moving::##arg_action_name##); \ + nm.wallkick_spin = arg_wkspin +#define _MACRO_HASH_POS(arg_hash_table,arg_prefix) \ + arg_hash_table[##arg_prefix##y][##arg_prefix##s][##arg_prefix##x & GENMOV_W_MASK] + +namespace AI { + bool g_spin180 = false; + std::vector g_combo_attack; + bool g_allSpin = false; + bool g_softdrop = true; + + void setSpin180( bool enable ) { + g_spin180 = enable; + } + bool spin180Enable() { + return g_spin180; + } + bool softdropEnable() { + return g_softdrop; + } + + void setComboList( std::vector combolist ) { + g_combo_attack = combolist; + } + int getComboAttack( int combo ) { + if ( g_combo_attack.empty() ) return 0; + if ( combo >= (int)g_combo_attack.size() ) return g_combo_attack.back(); + return g_combo_attack[combo]; + } + void setAllSpin(bool allSpin) { + g_allSpin = allSpin; + } + void setSoftdrop( bool softdrop ) { + g_softdrop = softdrop; + } + bool isEnableAllSpin() { + return g_allSpin; + } + + void GenMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold) { + movs.clear(); + if ( field.isCollide(x, y, getGem(cur.num, cur.spin) ) ) { + return ; + } + char _hash[64][4][GENMOV_W_MASK+1] = {0}; + char _hash_drop[64][4][GENMOV_W_MASK+1] = {0}; + char (*hash)[4][GENMOV_W_MASK+1] = &_hash[gem_add_y]; + char (*hash_drop)[4][GENMOV_W_MASK+1] = &_hash_drop[gem_add_y]; + MovList q(1024); + +#ifdef SWITCH_USING_HEIGHT_OPT + // height of every column + int field_w = field.width(), field_h = field.height(); + int min_y[32] = {0}; + { + int beg_y = -5; + while ( field.row[beg_y] == 0 ) ++beg_y; + for ( int x = 0; x < field_w; ++x ) { + for ( int y = beg_y, ey = field_h + 1; y <= ey; ++y ) { + if ( field.row[y] & ( 1 << x ) ) { + min_y[x] = y; + break; + } + } + } + } +#endif + { + MovingSimple m; + m.x = x; + m.y = y; + m.spin = cur.spin; + m.wallkick_spin = 0; + if ( hold ) { + m.lastmove = MovingSimple::MOV_HOLD; + m.hold = true; + } else { + m.lastmove = MovingSimple::MOV_NULL; + m.hold = false; + } + q.push(m); + hash[m.y][m.spin][m.x & GENMOV_W_MASK] = 1; + } + while ( ! q.empty() ) { + MovingSimple m; + q.pop(m); + if ( m.y < -1 ) continue; + if ( m.lastmove == MovingSimple::MOV_DROP ) { + movs.push_back(m); + continue; + } + + if ( m.lastmove != MovingSimple::MOV_DD && m.lastmove != MovingSimple::MOV_D ) + { + int nx = m.x, ny = m.y, ns = m.spin; + int wallkick_spin = m.wallkick_spin; +#ifndef SWITCH_USING_HEIGHT_OPT + while ( field.row[ny + cur.geth()] == 0 && ny + cur.geth() <= field.height() ) { // ·Ç¿ÕÆøÐвÅÄÜʹÓõÄÓÅ»¯ + ++ny; wallkick_spin = 0; + } + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + if ( !USING_MOV_D && _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_HASH_POS(hash, n) = 1; + } + ++ny; wallkick_spin = 0; + } +#endif +#ifdef SWITCH_USING_HEIGHT_OPT + { + int dist_min = 0x7fffffff; + for ( int x = 0; x < 4; ++x ) { + if ( getGemColH(cur.num, ns, x) ) { // 0 = empty column + int dist_cur_col = min_y[nx + x] - (ny + getGemColH(cur.num, ns, x)); + if ( dist_cur_col < dist_min ) { + dist_min = dist_cur_col; + } + } + } + if ( dist_min < 0 ) { // underground + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + if ( !USING_MOV_D && _MACRO_HASH_POS(hash,n) == 0) { + _MACRO_HASH_POS(hash,n) = 1; + } + ++ny; wallkick_spin = 0; + } + } else { // under the sun + ny = ny + dist_min; + if ( dist_min > 0 ) wallkick_spin = 0; + for ( int y = m.y + 1; y < ny; ++y ) { + if ( !USING_MOV_D && hash[y][ns][nx & GENMOV_W_MASK] == 0) { + hash[y][ns][nx & GENMOV_W_MASK] = 1; + } + } + } + } +#endif + { + int v_spin = ((isEnableAllSpin() || cur.num == GEMTYPE_T)) ? wallkick_spin : 0; + if ( (_MACRO_HASH_POS(hash_drop, n) & ( 1 << v_spin)) == 0 ) + { + + int _nx = nx, _ny = ny, _ns = ns; + + if ( cur.num == GEMTYPE_I || cur.num == GEMTYPE_Z || cur.num == GEMTYPE_S ) { + if ( ns == GEMTYPE_T ) { + _ny = ny + 1; + _ns = 0; + } + if ( ns == 3 ) { + _nx = nx + 1; + _ns = 1; + } + } + + if ( (_MACRO_HASH_POS(hash_drop, _n) & ( 1 << v_spin)) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_DROP, wallkick_spin); + if ( wallkick_spin ) _MACRO_HASH_POS(hash_drop, _n) |= 1 << wallkick_spin; + else _MACRO_HASH_POS(hash_drop, _n) |= 1; + q.push(nm); + } + } + if ( softdropEnable() ) // DD + { + if ( ny != y ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_DD, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + --nx; + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_L, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + if ( m.lastmove != MovingSimple::MOV_L && m.lastmove != MovingSimple::MOV_R + && m.lastmove != MovingSimple::MOV_LL && m.lastmove != MovingSimple::MOV_RR ) + { + int nx = m.x - 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx - 1, ny, getGem(cur.num, ns) ) ) { + --nx; + } + if ( nx != x && _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LL, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + ++nx; + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_R, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + if ( m.lastmove != MovingSimple::MOV_L && m.lastmove != MovingSimple::MOV_R + && m.lastmove != MovingSimple::MOV_LL && m.lastmove != MovingSimple::MOV_RR ) + { + int nx = m.x + 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx + 1, ny, getGem(cur.num, ns) ) ) { + ++nx; + } + if ( nx != x && _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RR, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } +#if USING_MOV_D > 0 + if ( m.lastmove != MovingSimple::MOV_DD ) + { + int nx = m.x, ny = m.y, ns = m.spin; + ++ny; + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_D, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } +#endif + { + int nx = m.x, ny = m.y, ns = (m.spin + 1) % cur.mod; + if ( ns != m.spin ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LSPIN, 1); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 0 ) ) { + if ( _MACRO_HASH_POS(hash, n) == 0 || cur.num == 2 && _MACRO_HASH_POS(hash, n) != 2 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LSPIN, 2); + _MACRO_HASH_POS(hash, n) = 2; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = (m.spin + 3) % cur.mod; + if ( ns != m.spin ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RSPIN, 1); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 1 ) ) { + if ( _MACRO_HASH_POS(hash, n) == 0 || cur.num == 2 && _MACRO_HASH_POS(hash, n) != 2 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RSPIN, 2); + _MACRO_HASH_POS(hash, n) = 2; + q.push(nm); + } + } + } + } + } + if ( spin180Enable() && m.lastmove != MovingSimple::MOV_SPIN2 ) // no 180 wallkick only + { + int nx = m.x, ny = m.y, ns = (m.spin + 2) % cur.mod; + if ( ns != m.spin ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_SPIN2, 1); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + } + + void FindPathMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold) { + movs.clear(); + if ( field.isCollide(x, y, getGem(cur.num, cur.spin) ) ) { + return ; + } + char _hash[64][4][GENMOV_W_MASK+1] = {0}; + char _hash_drop[64][4][GENMOV_W_MASK+1] = {0}; + char (*hash)[4][GENMOV_W_MASK+1] = &_hash[gem_add_y]; + char (*hash_drop)[4][GENMOV_W_MASK+1] = &_hash_drop[gem_add_y]; + MovList q(1024); + { + Moving m; + m.x = x; + m.y = y; + m.spin = cur.spin; + m.wallkick_spin = 0; + if ( hold ) { + m.movs.push_back(Moving::MOV_HOLD); + } else { + m.movs.push_back(Moving::MOV_NULL); + } + m.score = 0; + q.push(m); + hash[m.y][m.spin][m.x & GENMOV_W_MASK] = 1; + } + while ( ! q.empty() ) { + Moving m; + q.pop(m); + if ( m.y < -1 ) continue; + if ( m.movs.back() == Moving::MOV_DROP) { + movs.push_back(m); + continue; + } + + if ( m.movs.back() != Moving::MOV_DD && m.movs.back() != Moving::MOV_D) + { + int nx = m.x, ny = m.y, ns = m.spin; + int wallkick_spin = m.wallkick_spin; + //while ( field.row[ny + cur.geth()] == 0 && ny + cur.geth() <= field.height() ) { // ·Ç¿ÕÆøÐвÅÄÜʹÓõÄÓÅ»¯ + // ++ny; wallkick_spin = 0; + //} + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + //if ( !USING_MOV_D && _MACRO_HASH_POS(hash, n) == 0) { + // _MACRO_HASH_POS(hash, n) = 1; + //} + ++ny; wallkick_spin = 0; + } + { + int v_spin = ((isEnableAllSpin() || cur.num == GEMTYPE_T)) ? wallkick_spin : 0; + if ( (_MACRO_HASH_POS(hash_drop, n) & ( 1 << v_spin)) == 0 ) + { + int _nx = nx, _ny = ny, _ns = ns; + if ( (_MACRO_HASH_POS(hash_drop, _n) & ( 1 << v_spin)) == 0 ) { + _MACRO_CREATE_MOVING(MOV_DROP, wallkick_spin); + if ( wallkick_spin ) _MACRO_HASH_POS(hash_drop, _n) |= 1 << wallkick_spin; + else _MACRO_HASH_POS(hash_drop, _n) |= 1; + q.push(nm); + } + } + if ( softdropEnable() ) { + if ( ny != y ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVING(MOV_DD, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + --nx; + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_L, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + if ( m.movs.back() != Moving::MOV_L && m.movs.back() != Moving::MOV_R + && m.movs.back() != Moving::MOV_LL && m.movs.back() != Moving::MOV_RR ) + { + int nx = m.x - 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx - 1, ny, getGem(cur.num, ns) ) ) { + --nx; + } + if ( nx != x && _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVING(MOV_LL, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + ++nx; + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_R, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + if ( m.movs.back() != Moving::MOV_L && m.movs.back() != Moving::MOV_R + && m.movs.back() != Moving::MOV_LL && m.movs.back() != Moving::MOV_RR ) + { + int nx = m.x + 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx + 1, ny, getGem(cur.num, ns) ) ) { + ++nx; + } + if ( nx != x && _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVING(MOV_RR, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + //if (USING_MOV_D) + if ( m.movs.back() != Moving::MOV_DD ) + { + int nx = m.x, ny = m.y, ns = m.spin; + ++ny; + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_D, 0); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + { + int nx = m.x, ny = m.y, ns = (m.spin + 1) % cur.mod; + if ( ns != m.spin ) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVING(MOV_LSPIN, 1); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 0 ) ) { + if ( _MACRO_HASH_POS(hash, n) == 0 || cur.num == 2 && _MACRO_HASH_POS(hash, n) != 2 ) { + _MACRO_CREATE_MOVING(MOV_LSPIN, 2); + _MACRO_HASH_POS(hash, n) = 2; + q.push(nm); + } + } + } + } + { + int nx = m.x, ny = m.y, ns = (m.spin + 3) % cur.mod; + if ( ns != m.spin ) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + _MACRO_CREATE_MOVING(MOV_RSPIN, 1); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 1 ) ) { + if ( _MACRO_HASH_POS(hash, n) == 0 || cur.num == 2 && _MACRO_HASH_POS(hash, n) != 2 ) { + _MACRO_CREATE_MOVING(MOV_RSPIN, 2); + _MACRO_HASH_POS(hash, n) = 2; + q.push(nm); + } + } + } + } + if ( spin180Enable() ) //&& m.movs.back() != Moving::MOV_SPIN2 ) // no 180 wallkick only + { + int nx = m.x, ny = m.y, ns = (m.spin + 2) % cur.mod; + if ( ns != m.spin ) { + if ( _MACRO_HASH_POS(hash, n) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_SPIN2, 1); + _MACRO_HASH_POS(hash, n) = 1; + q.push(nm); + } + } + } + } + } + } +} diff --git a/dllai/targetver.h b/dllai/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/dllai/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/dllai/tetris_gem.cpp b/dllai/tetris_gem.cpp new file mode 100644 index 0000000..653a3b5 --- /dev/null +++ b/dllai/tetris_gem.cpp @@ -0,0 +1,81 @@ +#include "tetris_gem.h" + +namespace AI { + Gem gems[8][4]; + int GEM_COL_H[8][4][4]; + + class init_obj { + public: + init_obj() { + Gem _gems[8][4] = { + { + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + }, + { // I 1 + { 0,15, 0, 0}, + { 2, 2, 2, 2}, + { 0, 0,15, 0}, + { 4, 4, 4, 4}, + }, + { // T 2 + { 2, 7, 0, 0}, + { 2, 3, 2, 0}, + { 0, 7, 2, 0}, + { 2, 6, 2, 0}, + }, + { // L 3 + { 4, 7, 0, 0}, + { 3, 2, 2, 0}, + { 0, 7, 1, 0}, + { 2, 2, 6, 0}, + }, + { // J 4 + { 1, 7, 0, 0}, + { 2, 2, 3, 0}, + { 0, 7, 4, 0}, + { 6, 2, 2, 0}, + }, + { // Z 5 + { 3, 6, 0, 0}, + { 2, 3, 1, 0}, + { 0, 3, 6, 0}, + { 4, 6, 2, 0}, + }, + { // S 6 + { 6, 3, 0, 0}, + { 1, 3, 2, 0}, + { 0, 6, 3, 0}, + { 2, 6, 4, 0}, + }, + { // O 7 + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + }, + }; + int m[] = {1, 4, 4, 4, 4, 4, 4, 1}; // TOP SRS + for ( int n = 0; n < 8; ++n) { + for ( int s = 0; s < 4; ++s) { + gems[n][s] = _gems[n][s]; + gems[n][s].num = n; + gems[n][s].mod = m[n]; + gems[n][s].spin = s % m[n]; + //GEM_COL_H Initialization + for ( int x = 0; x < 4; ++x) { + GEM_COL_H[n][s][x] = 0; + for ( int y = 0; y < 4; ++y) { + if(gems[n][s].bitmap[y] & (1 << x)) { + GEM_COL_H[n][s][x] = y + 1; + //I vertical: 0 4 0 0 + } + } + } + } + } + } + } _o; +} diff --git a/dllai/tetris_gem.h b/dllai/tetris_gem.h new file mode 100644 index 0000000..34689a1 --- /dev/null +++ b/dllai/tetris_gem.h @@ -0,0 +1,34 @@ +#pragma once +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + +#if _MSC_VER <= 1200 +#define for if (0); else for +#pragma warning(disable : 4786) +#endif + +namespace AI { + enum GemType { + GEMTYPE_NULL, + GEMTYPE_I, + GEMTYPE_T, + GEMTYPE_L, + GEMTYPE_J, + GEMTYPE_Z, + GEMTYPE_S, + GEMTYPE_O, + }; + struct Gem { + unsigned long bitmap[4]; + int num, spin, mod; + int geth() const { return 4; } + }; + inline Gem& getGem( int number, int spin ) { + extern Gem gems[8][4]; + return gems[number][spin]; + } + inline int getGemColH( int number, int spin, int x) { + extern int GEM_COL_H[8][4][4]; + return GEM_COL_H[number][spin][x]; + } + +} diff --git a/tetris_ai/Profile.cpp b/tetris_ai/Profile.cpp new file mode 100644 index 0000000..27d305b --- /dev/null +++ b/tetris_ai/Profile.cpp @@ -0,0 +1,19 @@ +#include "Profile.h" + + +CProfile::CProfile(void) +{ + char buff[1024]; + m_errcode = 0; + ::GetCurrentDirectoryA(1024, buff); + m_path = buff; + char back = m_path[m_path.size()-1]; + if ( back != '\\' && back != '/' ) { + m_path += '\\'; + } +} + + +CProfile::~CProfile(void) +{ +} diff --git a/tetris_ai/Profile.h b/tetris_ai/Profile.h new file mode 100644 index 0000000..dfbb792 --- /dev/null +++ b/tetris_ai/Profile.h @@ -0,0 +1,53 @@ +#pragma once +#include "windows.h" +#include + +class CProfile +{ +public: + CProfile(void) ; + virtual ~CProfile(void); + void SetFile( std::string name ) { + m_filename = name; + } + void SetSection( std::string section ) { + m_section = section; + } + int WriteString( std::string key, std::string value ) { + return WritePrivateProfileStringA( m_section.c_str(), key.c_str(), value.c_str(), (m_path + m_filename).c_str() ); + } + int WriteInteger( std::string key, int value ) { + char buff[1024]; + sprintf( buff, "%d", value ); + return WriteString( key, buff ); + } + int ReadString( std::string key, std::string& value ) { + char buff[1024]; + int ret = GetPrivateProfileStringA( m_section.c_str(), key.c_str(), "", buff, 1024, (m_path + m_filename).c_str() ); + if ( ret > 0 ) value = buff; + return ret; + } + bool IsInteger( std::string key ) { + ReadInteger( key ); + return m_errcode == 0; + } + int ReadInteger( std::string key ) { + std::string s; + if ( ReadString( key, s ) > 0 ) { + int ret; + if ( sscanf( s.c_str(), "%d", &ret ) > 0 ) { + m_errcode = 0; + return ret; + } + m_errcode = 1; + } + m_errcode = 2; + return 0; + } +private: + int m_errcode; + std::string m_path; + std::string m_filename; + std::string m_section; +}; + diff --git a/tetris_ai/Tspin.sln b/tetris_ai/Tspin.sln new file mode 100644 index 0000000..f99cf13 --- /dev/null +++ b/tetris_ai/Tspin.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tspin", "tetris_ai.vcxproj", "{1063E110-F488-4ADA-8632-AB6E53E8BEDD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dllai", "..\dllai\dllai.vcxproj", "{AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + XP_Release|Win32 = XP_Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1063E110-F488-4ADA-8632-AB6E53E8BEDD}.Debug|Win32.ActiveCfg = Debug|Win32 + {1063E110-F488-4ADA-8632-AB6E53E8BEDD}.Debug|Win32.Build.0 = Debug|Win32 + {1063E110-F488-4ADA-8632-AB6E53E8BEDD}.Release|Win32.ActiveCfg = Release|Win32 + {1063E110-F488-4ADA-8632-AB6E53E8BEDD}.Release|Win32.Build.0 = Release|Win32 + {1063E110-F488-4ADA-8632-AB6E53E8BEDD}.XP_Release|Win32.ActiveCfg = XP_Release|Win32 + {1063E110-F488-4ADA-8632-AB6E53E8BEDD}.XP_Release|Win32.Build.0 = XP_Release|Win32 + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}.Debug|Win32.ActiveCfg = Debug|Win32 + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}.Debug|Win32.Build.0 = Debug|Win32 + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}.Release|Win32.ActiveCfg = Release|Win32 + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}.Release|Win32.Build.0 = Release|Win32 + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}.XP_Release|Win32.ActiveCfg = XP_Release|Win32 + {AFFA8B7C-EAAA-46F7-92B2-1A1F422B1EF3}.XP_Release|Win32.Build.0 = XP_Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection +EndGlobal diff --git a/tetris_ai/Tspin08.rc b/tetris_ai/Tspin08.rc new file mode 100644 index 0000000..0dbf7a5 --- /dev/null +++ b/tetris_ai/Tspin08.rc @@ -0,0 +1,72 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource1.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Chinese (P.R.C.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) +#ifdef _WIN32 +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +#pragma code_page(936) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource1.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "tetris.ico" +#endif // Chinese (P.R.C.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/tetris_ai/Tspin08.sln b/tetris_ai/Tspin08.sln new file mode 100644 index 0000000..ddff32c --- /dev/null +++ b/tetris_ai/Tspin08.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tspin08", "Tspin08.vcproj", "{5F5C2F45-C92B-4EAA-A48C-3325B390CA29}" + ProjectSection(ProjectDependencies) = postProject + {90396E54-F76B-4A1D-9291-720FDA200388} = {90396E54-F76B-4A1D-9291-720FDA200388} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dllai", "..\dllai\dllai.vcproj", "{90396E54-F76B-4A1D-9291-720FDA200388}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5F5C2F45-C92B-4EAA-A48C-3325B390CA29}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F5C2F45-C92B-4EAA-A48C-3325B390CA29}.Debug|Win32.Build.0 = Debug|Win32 + {5F5C2F45-C92B-4EAA-A48C-3325B390CA29}.Release|Win32.ActiveCfg = Release|Win32 + {5F5C2F45-C92B-4EAA-A48C-3325B390CA29}.Release|Win32.Build.0 = Release|Win32 + {90396E54-F76B-4A1D-9291-720FDA200388}.Debug|Win32.ActiveCfg = Debug|Win32 + {90396E54-F76B-4A1D-9291-720FDA200388}.Debug|Win32.Build.0 = Debug|Win32 + {90396E54-F76B-4A1D-9291-720FDA200388}.Release|Win32.ActiveCfg = Release|Win32 + {90396E54-F76B-4A1D-9291-720FDA200388}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tetris_ai/Tspin08.vcproj b/tetris_ai/Tspin08.vcproj new file mode 100644 index 0000000..7763103 --- /dev/null +++ b/tetris_ai/Tspin08.vcproj @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tetris_ai/bass.h b/tetris_ai/bass.h new file mode 100644 index 0000000..163664b --- /dev/null +++ b/tetris_ai/bass.h @@ -0,0 +1,990 @@ +/* + BASS 2.4 C/C++ header file + Copyright (c) 1999-2013 Un4seen Developments Ltd. + + See the BASS.CHM file for more detailed documentation +*/ + +#ifndef BASS_H +#define BASS_H + +#ifdef _WIN32 +#include +typedef unsigned __int64 QWORD; +#else +#include +#define WINAPI +#define CALLBACK +typedef uint8_t BYTE; +typedef uint16_t WORD; +typedef uint32_t DWORD; +typedef uint64_t QWORD; +#ifndef __OBJC__ +typedef int BOOL; +#endif +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#define LOBYTE(a) (BYTE)(a) +#define HIBYTE(a) (BYTE)((a)>>8) +#define LOWORD(a) (WORD)(a) +#define HIWORD(a) (WORD)((a)>>16) +#define MAKEWORD(a,b) (WORD)(((a)&0xff)|((b)<<8)) +#define MAKELONG(a,b) (DWORD)(((a)&0xffff)|((b)<<16)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define BASSVERSION 0x204 // API version +#define BASSVERSIONTEXT "2.4" + +#ifndef BASSDEF +#define BASSDEF(f) WINAPI f +#endif + +typedef DWORD HMUSIC; // MOD music handle +typedef DWORD HSAMPLE; // sample handle +typedef DWORD HCHANNEL; // playing sample's channel handle +typedef DWORD HSTREAM; // sample stream handle +typedef DWORD HRECORD; // recording handle +typedef DWORD HSYNC; // synchronizer handle +typedef DWORD HDSP; // DSP handle +typedef DWORD HFX; // DX8 effect handle +typedef DWORD HPLUGIN; // Plugin handle + +// Error codes returned by BASS_ErrorGetCode +#define BASS_OK 0 // all is OK +#define BASS_ERROR_MEM 1 // memory error +#define BASS_ERROR_FILEOPEN 2 // can't open the file +#define BASS_ERROR_DRIVER 3 // can't find a free/valid driver +#define BASS_ERROR_BUFLOST 4 // the sample buffer was lost +#define BASS_ERROR_HANDLE 5 // invalid handle +#define BASS_ERROR_FORMAT 6 // unsupported sample format +#define BASS_ERROR_POSITION 7 // invalid position +#define BASS_ERROR_INIT 8 // BASS_Init has not been successfully called +#define BASS_ERROR_START 9 // BASS_Start has not been successfully called +#define BASS_ERROR_ALREADY 14 // already initialized/paused/whatever +#define BASS_ERROR_NOCHAN 18 // can't get a free channel +#define BASS_ERROR_ILLTYPE 19 // an illegal type was specified +#define BASS_ERROR_ILLPARAM 20 // an illegal parameter was specified +#define BASS_ERROR_NO3D 21 // no 3D support +#define BASS_ERROR_NOEAX 22 // no EAX support +#define BASS_ERROR_DEVICE 23 // illegal device number +#define BASS_ERROR_NOPLAY 24 // not playing +#define BASS_ERROR_FREQ 25 // illegal sample rate +#define BASS_ERROR_NOTFILE 27 // the stream is not a file stream +#define BASS_ERROR_NOHW 29 // no hardware voices available +#define BASS_ERROR_EMPTY 31 // the MOD music has no sequence data +#define BASS_ERROR_NONET 32 // no internet connection could be opened +#define BASS_ERROR_CREATE 33 // couldn't create the file +#define BASS_ERROR_NOFX 34 // effects are not available +#define BASS_ERROR_NOTAVAIL 37 // requested data is not available +#define BASS_ERROR_DECODE 38 // the channel is/isn't a "decoding channel" +#define BASS_ERROR_DX 39 // a sufficient DirectX version is not installed +#define BASS_ERROR_TIMEOUT 40 // connection timedout +#define BASS_ERROR_FILEFORM 41 // unsupported file format +#define BASS_ERROR_SPEAKER 42 // unavailable speaker +#define BASS_ERROR_VERSION 43 // invalid BASS version (used by add-ons) +#define BASS_ERROR_CODEC 44 // codec is not available/supported +#define BASS_ERROR_ENDED 45 // the channel/file has ended +#define BASS_ERROR_BUSY 46 // the device is busy +#define BASS_ERROR_UNKNOWN -1 // some other mystery problem + +// BASS_SetConfig options +#define BASS_CONFIG_BUFFER 0 +#define BASS_CONFIG_UPDATEPERIOD 1 +#define BASS_CONFIG_GVOL_SAMPLE 4 +#define BASS_CONFIG_GVOL_STREAM 5 +#define BASS_CONFIG_GVOL_MUSIC 6 +#define BASS_CONFIG_CURVE_VOL 7 +#define BASS_CONFIG_CURVE_PAN 8 +#define BASS_CONFIG_FLOATDSP 9 +#define BASS_CONFIG_3DALGORITHM 10 +#define BASS_CONFIG_NET_TIMEOUT 11 +#define BASS_CONFIG_NET_BUFFER 12 +#define BASS_CONFIG_PAUSE_NOPLAY 13 +#define BASS_CONFIG_NET_PREBUF 15 +#define BASS_CONFIG_NET_PASSIVE 18 +#define BASS_CONFIG_REC_BUFFER 19 +#define BASS_CONFIG_NET_PLAYLIST 21 +#define BASS_CONFIG_MUSIC_VIRTUAL 22 +#define BASS_CONFIG_VERIFY 23 +#define BASS_CONFIG_UPDATETHREADS 24 +#define BASS_CONFIG_DEV_BUFFER 27 +#define BASS_CONFIG_VISTA_TRUEPOS 30 +#define BASS_CONFIG_IOS_MIXAUDIO 34 +#define BASS_CONFIG_DEV_DEFAULT 36 +#define BASS_CONFIG_NET_READTIMEOUT 37 +#define BASS_CONFIG_VISTA_SPEAKERS 38 +#define BASS_CONFIG_IOS_SPEAKER 39 +#define BASS_CONFIG_HANDLES 41 +#define BASS_CONFIG_UNICODE 42 +#define BASS_CONFIG_SRC 43 +#define BASS_CONFIG_SRC_SAMPLE 44 +#define BASS_CONFIG_ASYNCFILE_BUFFER 45 +#define BASS_CONFIG_OGG_PRESCAN 47 + +// BASS_SetConfigPtr options +#define BASS_CONFIG_NET_AGENT 16 +#define BASS_CONFIG_NET_PROXY 17 +#define BASS_CONFIG_IOS_NOTIFY 46 + +// BASS_Init flags +#define BASS_DEVICE_8BITS 1 // 8 bit resolution, else 16 bit +#define BASS_DEVICE_MONO 2 // mono, else stereo +#define BASS_DEVICE_3D 4 // enable 3D functionality +#define BASS_DEVICE_LATENCY 0x100 // calculate device latency (BASS_INFO struct) +#define BASS_DEVICE_CPSPEAKERS 0x400 // detect speakers via Windows control panel +#define BASS_DEVICE_SPEAKERS 0x800 // force enabling of speaker assignment +#define BASS_DEVICE_NOSPEAKER 0x1000 // ignore speaker arrangement +#define BASS_DEVICE_DMIX 0x2000 // use ALSA "dmix" plugin +#define BASS_DEVICE_FREQ 0x4000 // set device sample rate + +// DirectSound interfaces (for use with BASS_GetDSoundObject) +#define BASS_OBJECT_DS 1 // IDirectSound +#define BASS_OBJECT_DS3DL 2 // IDirectSound3DListener + +// Device info structure +typedef struct { +#ifdef _WIN32_WCE + const wchar_t *name; // description + const wchar_t *driver; // driver +#else + const char *name; // description + const char *driver; // driver +#endif + DWORD flags; +} BASS_DEVICEINFO; + +// BASS_DEVICEINFO flags +#define BASS_DEVICE_ENABLED 1 +#define BASS_DEVICE_DEFAULT 2 +#define BASS_DEVICE_INIT 4 + +typedef struct { + DWORD flags; // device capabilities (DSCAPS_xxx flags) + DWORD hwsize; // size of total device hardware memory + DWORD hwfree; // size of free device hardware memory + DWORD freesam; // number of free sample slots in the hardware + DWORD free3d; // number of free 3D sample slots in the hardware + DWORD minrate; // min sample rate supported by the hardware + DWORD maxrate; // max sample rate supported by the hardware + BOOL eax; // device supports EAX? (always FALSE if BASS_DEVICE_3D was not used) + DWORD minbuf; // recommended minimum buffer length in ms (requires BASS_DEVICE_LATENCY) + DWORD dsver; // DirectSound version + DWORD latency; // delay (in ms) before start of playback (requires BASS_DEVICE_LATENCY) + DWORD initflags; // BASS_Init "flags" parameter + DWORD speakers; // number of speakers available + DWORD freq; // current output rate +} BASS_INFO; + +// BASS_INFO flags (from DSOUND.H) +#define DSCAPS_CONTINUOUSRATE 0x00000010 // supports all sample rates between min/maxrate +#define DSCAPS_EMULDRIVER 0x00000020 // device does NOT have hardware DirectSound support +#define DSCAPS_CERTIFIED 0x00000040 // device driver has been certified by Microsoft +#define DSCAPS_SECONDARYMONO 0x00000100 // mono +#define DSCAPS_SECONDARYSTEREO 0x00000200 // stereo +#define DSCAPS_SECONDARY8BIT 0x00000400 // 8 bit +#define DSCAPS_SECONDARY16BIT 0x00000800 // 16 bit + +// Recording device info structure +typedef struct { + DWORD flags; // device capabilities (DSCCAPS_xxx flags) + DWORD formats; // supported standard formats (WAVE_FORMAT_xxx flags) + DWORD inputs; // number of inputs + BOOL singlein; // TRUE = only 1 input can be set at a time + DWORD freq; // current input rate +} BASS_RECORDINFO; + +// BASS_RECORDINFO flags (from DSOUND.H) +#define DSCCAPS_EMULDRIVER DSCAPS_EMULDRIVER // device does NOT have hardware DirectSound recording support +#define DSCCAPS_CERTIFIED DSCAPS_CERTIFIED // device driver has been certified by Microsoft + +// defines for formats field of BASS_RECORDINFO (from MMSYSTEM.H) +#ifndef WAVE_FORMAT_1M08 +#define WAVE_FORMAT_1M08 0x00000001 /* 11.025 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_1S08 0x00000002 /* 11.025 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_1M16 0x00000004 /* 11.025 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_1S16 0x00000008 /* 11.025 kHz, Stereo, 16-bit */ +#define WAVE_FORMAT_2M08 0x00000010 /* 22.05 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_2S08 0x00000020 /* 22.05 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_2M16 0x00000040 /* 22.05 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_2S16 0x00000080 /* 22.05 kHz, Stereo, 16-bit */ +#define WAVE_FORMAT_4M08 0x00000100 /* 44.1 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_4S08 0x00000200 /* 44.1 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_4M16 0x00000400 /* 44.1 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_4S16 0x00000800 /* 44.1 kHz, Stereo, 16-bit */ +#endif + +// Sample info structure +typedef struct { + DWORD freq; // default playback rate + float volume; // default volume (0-1) + float pan; // default pan (-1=left, 0=middle, 1=right) + DWORD flags; // BASS_SAMPLE_xxx flags + DWORD length; // length (in bytes) + DWORD max; // maximum simultaneous playbacks + DWORD origres; // original resolution bits + DWORD chans; // number of channels + DWORD mingap; // minimum gap (ms) between creating channels + DWORD mode3d; // BASS_3DMODE_xxx mode + float mindist; // minimum distance + float maxdist; // maximum distance + DWORD iangle; // angle of inside projection cone + DWORD oangle; // angle of outside projection cone + float outvol; // delta-volume outside the projection cone + DWORD vam; // voice allocation/management flags (BASS_VAM_xxx) + DWORD priority; // priority (0=lowest, 0xffffffff=highest) +} BASS_SAMPLE; + +#define BASS_SAMPLE_8BITS 1 // 8 bit +#define BASS_SAMPLE_FLOAT 256 // 32-bit floating-point +#define BASS_SAMPLE_MONO 2 // mono +#define BASS_SAMPLE_LOOP 4 // looped +#define BASS_SAMPLE_3D 8 // 3D functionality +#define BASS_SAMPLE_SOFTWARE 16 // not using hardware mixing +#define BASS_SAMPLE_MUTEMAX 32 // mute at max distance (3D only) +#define BASS_SAMPLE_VAM 64 // DX7 voice allocation & management +#define BASS_SAMPLE_FX 128 // old implementation of DX8 effects +#define BASS_SAMPLE_OVER_VOL 0x10000 // override lowest volume +#define BASS_SAMPLE_OVER_POS 0x20000 // override longest playing +#define BASS_SAMPLE_OVER_DIST 0x30000 // override furthest from listener (3D only) + +#define BASS_STREAM_PRESCAN 0x20000 // enable pin-point seeking/length (MP3/MP2/MP1) +#define BASS_MP3_SETPOS BASS_STREAM_PRESCAN +#define BASS_STREAM_AUTOFREE 0x40000 // automatically free the stream when it stop/ends +#define BASS_STREAM_RESTRATE 0x80000 // restrict the download rate of internet file streams +#define BASS_STREAM_BLOCK 0x100000 // download/play internet file stream in small blocks +#define BASS_STREAM_DECODE 0x200000 // don't play the stream, only decode (BASS_ChannelGetData) +#define BASS_STREAM_STATUS 0x800000 // give server status info (HTTP/ICY tags) in DOWNLOADPROC + +#define BASS_MUSIC_FLOAT BASS_SAMPLE_FLOAT +#define BASS_MUSIC_MONO BASS_SAMPLE_MONO +#define BASS_MUSIC_LOOP BASS_SAMPLE_LOOP +#define BASS_MUSIC_3D BASS_SAMPLE_3D +#define BASS_MUSIC_FX BASS_SAMPLE_FX +#define BASS_MUSIC_AUTOFREE BASS_STREAM_AUTOFREE +#define BASS_MUSIC_DECODE BASS_STREAM_DECODE +#define BASS_MUSIC_PRESCAN BASS_STREAM_PRESCAN // calculate playback length +#define BASS_MUSIC_CALCLEN BASS_MUSIC_PRESCAN +#define BASS_MUSIC_RAMP 0x200 // normal ramping +#define BASS_MUSIC_RAMPS 0x400 // sensitive ramping +#define BASS_MUSIC_SURROUND 0x800 // surround sound +#define BASS_MUSIC_SURROUND2 0x1000 // surround sound (mode 2) +#define BASS_MUSIC_FT2MOD 0x2000 // play .MOD as FastTracker 2 does +#define BASS_MUSIC_PT1MOD 0x4000 // play .MOD as ProTracker 1 does +#define BASS_MUSIC_NONINTER 0x10000 // non-interpolated sample mixing +#define BASS_MUSIC_SINCINTER 0x800000 // sinc interpolated sample mixing +#define BASS_MUSIC_POSRESET 0x8000 // stop all notes when moving position +#define BASS_MUSIC_POSRESETEX 0x400000 // stop all notes and reset bmp/etc when moving position +#define BASS_MUSIC_STOPBACK 0x80000 // stop the music on a backwards jump effect +#define BASS_MUSIC_NOSAMPLE 0x100000 // don't load the samples + +// Speaker assignment flags +#define BASS_SPEAKER_FRONT 0x1000000 // front speakers +#define BASS_SPEAKER_REAR 0x2000000 // rear/side speakers +#define BASS_SPEAKER_CENLFE 0x3000000 // center & LFE speakers (5.1) +#define BASS_SPEAKER_REAR2 0x4000000 // rear center speakers (7.1) +#define BASS_SPEAKER_N(n) ((n)<<24) // n'th pair of speakers (max 15) +#define BASS_SPEAKER_LEFT 0x10000000 // modifier: left +#define BASS_SPEAKER_RIGHT 0x20000000 // modifier: right +#define BASS_SPEAKER_FRONTLEFT BASS_SPEAKER_FRONT|BASS_SPEAKER_LEFT +#define BASS_SPEAKER_FRONTRIGHT BASS_SPEAKER_FRONT|BASS_SPEAKER_RIGHT +#define BASS_SPEAKER_REARLEFT BASS_SPEAKER_REAR|BASS_SPEAKER_LEFT +#define BASS_SPEAKER_REARRIGHT BASS_SPEAKER_REAR|BASS_SPEAKER_RIGHT +#define BASS_SPEAKER_CENTER BASS_SPEAKER_CENLFE|BASS_SPEAKER_LEFT +#define BASS_SPEAKER_LFE BASS_SPEAKER_CENLFE|BASS_SPEAKER_RIGHT +#define BASS_SPEAKER_REAR2LEFT BASS_SPEAKER_REAR2|BASS_SPEAKER_LEFT +#define BASS_SPEAKER_REAR2RIGHT BASS_SPEAKER_REAR2|BASS_SPEAKER_RIGHT + +#define BASS_ASYNCFILE 0x40000000 +#define BASS_UNICODE 0x80000000 + +#define BASS_RECORD_PAUSE 0x8000 // start recording paused + +// DX7 voice allocation & management flags +#define BASS_VAM_HARDWARE 1 +#define BASS_VAM_SOFTWARE 2 +#define BASS_VAM_TERM_TIME 4 +#define BASS_VAM_TERM_DIST 8 +#define BASS_VAM_TERM_PRIO 16 + +// Channel info structure +typedef struct { + DWORD freq; // default playback rate + DWORD chans; // channels + DWORD flags; // BASS_SAMPLE/STREAM/MUSIC/SPEAKER flags + DWORD ctype; // type of channel + DWORD origres; // original resolution + HPLUGIN plugin; // plugin + HSAMPLE sample; // sample + const char *filename; // filename +} BASS_CHANNELINFO; + +// BASS_CHANNELINFO types +#define BASS_CTYPE_SAMPLE 1 +#define BASS_CTYPE_RECORD 2 +#define BASS_CTYPE_STREAM 0x10000 +#define BASS_CTYPE_STREAM_OGG 0x10002 +#define BASS_CTYPE_STREAM_MP1 0x10003 +#define BASS_CTYPE_STREAM_MP2 0x10004 +#define BASS_CTYPE_STREAM_MP3 0x10005 +#define BASS_CTYPE_STREAM_AIFF 0x10006 +#define BASS_CTYPE_STREAM_CA 0x10007 +#define BASS_CTYPE_STREAM_MF 0x10008 +#define BASS_CTYPE_STREAM_WAV 0x40000 // WAVE flag, LOWORD=codec +#define BASS_CTYPE_STREAM_WAV_PCM 0x50001 +#define BASS_CTYPE_STREAM_WAV_FLOAT 0x50003 +#define BASS_CTYPE_MUSIC_MOD 0x20000 +#define BASS_CTYPE_MUSIC_MTM 0x20001 +#define BASS_CTYPE_MUSIC_S3M 0x20002 +#define BASS_CTYPE_MUSIC_XM 0x20003 +#define BASS_CTYPE_MUSIC_IT 0x20004 +#define BASS_CTYPE_MUSIC_MO3 0x00100 // MO3 flag + +typedef struct { + DWORD ctype; // channel type +#ifdef _WIN32_WCE + const wchar_t *name; // format description + const wchar_t *exts; // file extension filter (*.ext1;*.ext2;etc...) +#else + const char *name; // format description + const char *exts; // file extension filter (*.ext1;*.ext2;etc...) +#endif +} BASS_PLUGINFORM; + +typedef struct { + DWORD version; // version (same form as BASS_GetVersion) + DWORD formatc; // number of formats + const BASS_PLUGINFORM *formats; // the array of formats +} BASS_PLUGININFO; + +// 3D vector (for 3D positions/velocities/orientations) +typedef struct BASS_3DVECTOR { +#ifdef __cplusplus + BASS_3DVECTOR() {}; + BASS_3DVECTOR(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}; +#endif + float x; // +=right, -=left + float y; // +=up, -=down + float z; // +=front, -=behind +} BASS_3DVECTOR; + +// 3D channel modes +#define BASS_3DMODE_NORMAL 0 // normal 3D processing +#define BASS_3DMODE_RELATIVE 1 // position is relative to the listener +#define BASS_3DMODE_OFF 2 // no 3D processing + +// software 3D mixing algorithms (used with BASS_CONFIG_3DALGORITHM) +#define BASS_3DALG_DEFAULT 0 +#define BASS_3DALG_OFF 1 +#define BASS_3DALG_FULL 2 +#define BASS_3DALG_LIGHT 3 + +// EAX environments, use with BASS_SetEAXParameters +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_COUNT // total number of environments +}; + +// EAX presets, usage: BASS_SetEAXParameters(EAX_PRESET_xxx) +#define EAX_PRESET_GENERIC EAX_ENVIRONMENT_GENERIC,0.5F,1.493F,0.5F +#define EAX_PRESET_PADDEDCELL EAX_ENVIRONMENT_PADDEDCELL,0.25F,0.1F,0.0F +#define EAX_PRESET_ROOM EAX_ENVIRONMENT_ROOM,0.417F,0.4F,0.666F +#define EAX_PRESET_BATHROOM EAX_ENVIRONMENT_BATHROOM,0.653F,1.499F,0.166F +#define EAX_PRESET_LIVINGROOM EAX_ENVIRONMENT_LIVINGROOM,0.208F,0.478F,0.0F +#define EAX_PRESET_STONEROOM EAX_ENVIRONMENT_STONEROOM,0.5F,2.309F,0.888F +#define EAX_PRESET_AUDITORIUM EAX_ENVIRONMENT_AUDITORIUM,0.403F,4.279F,0.5F +#define EAX_PRESET_CONCERTHALL EAX_ENVIRONMENT_CONCERTHALL,0.5F,3.961F,0.5F +#define EAX_PRESET_CAVE EAX_ENVIRONMENT_CAVE,0.5F,2.886F,1.304F +#define EAX_PRESET_ARENA EAX_ENVIRONMENT_ARENA,0.361F,7.284F,0.332F +#define EAX_PRESET_HANGAR EAX_ENVIRONMENT_HANGAR,0.5F,10.0F,0.3F +#define EAX_PRESET_CARPETEDHALLWAY EAX_ENVIRONMENT_CARPETEDHALLWAY,0.153F,0.259F,2.0F +#define EAX_PRESET_HALLWAY EAX_ENVIRONMENT_HALLWAY,0.361F,1.493F,0.0F +#define EAX_PRESET_STONECORRIDOR EAX_ENVIRONMENT_STONECORRIDOR,0.444F,2.697F,0.638F +#define EAX_PRESET_ALLEY EAX_ENVIRONMENT_ALLEY,0.25F,1.752F,0.776F +#define EAX_PRESET_FOREST EAX_ENVIRONMENT_FOREST,0.111F,3.145F,0.472F +#define EAX_PRESET_CITY EAX_ENVIRONMENT_CITY,0.111F,2.767F,0.224F +#define EAX_PRESET_MOUNTAINS EAX_ENVIRONMENT_MOUNTAINS,0.194F,7.841F,0.472F +#define EAX_PRESET_QUARRY EAX_ENVIRONMENT_QUARRY,1.0F,1.499F,0.5F +#define EAX_PRESET_PLAIN EAX_ENVIRONMENT_PLAIN,0.097F,2.767F,0.224F +#define EAX_PRESET_PARKINGLOT EAX_ENVIRONMENT_PARKINGLOT,0.208F,1.652F,1.5F +#define EAX_PRESET_SEWERPIPE EAX_ENVIRONMENT_SEWERPIPE,0.652F,2.886F,0.25F +#define EAX_PRESET_UNDERWATER EAX_ENVIRONMENT_UNDERWATER,1.0F,1.499F,0.0F +#define EAX_PRESET_DRUGGED EAX_ENVIRONMENT_DRUGGED,0.875F,8.392F,1.388F +#define EAX_PRESET_DIZZY EAX_ENVIRONMENT_DIZZY,0.139F,17.234F,0.666F +#define EAX_PRESET_PSYCHOTIC EAX_ENVIRONMENT_PSYCHOTIC,0.486F,7.563F,0.806F + +typedef DWORD (CALLBACK STREAMPROC)(HSTREAM handle, void *buffer, DWORD length, void *user); +/* User stream callback function. NOTE: A stream function should obviously be as quick +as possible, other streams (and MOD musics) can't be mixed until it's finished. +handle : The stream that needs writing +buffer : Buffer to write the samples in +length : Number of bytes to write +user : The 'user' parameter value given when calling BASS_StreamCreate +RETURN : Number of bytes written. Set the BASS_STREAMPROC_END flag to end + the stream. */ + +#define BASS_STREAMPROC_END 0x80000000 // end of user stream flag + +// special STREAMPROCs +#define STREAMPROC_DUMMY (STREAMPROC*)0 // "dummy" stream +#define STREAMPROC_PUSH (STREAMPROC*)-1 // push stream + +// BASS_StreamCreateFileUser file systems +#define STREAMFILE_NOBUFFER 0 +#define STREAMFILE_BUFFER 1 +#define STREAMFILE_BUFFERPUSH 2 + +// User file stream callback functions +typedef void (CALLBACK FILECLOSEPROC)(void *user); +typedef QWORD (CALLBACK FILELENPROC)(void *user); +typedef DWORD (CALLBACK FILEREADPROC)(void *buffer, DWORD length, void *user); +typedef BOOL (CALLBACK FILESEEKPROC)(QWORD offset, void *user); + +typedef struct { + FILECLOSEPROC *close; + FILELENPROC *length; + FILEREADPROC *read; + FILESEEKPROC *seek; +} BASS_FILEPROCS; + +// BASS_StreamPutFileData options +#define BASS_FILEDATA_END 0 // end & close the file + +// BASS_StreamGetFilePosition modes +#define BASS_FILEPOS_CURRENT 0 +#define BASS_FILEPOS_DECODE BASS_FILEPOS_CURRENT +#define BASS_FILEPOS_DOWNLOAD 1 +#define BASS_FILEPOS_END 2 +#define BASS_FILEPOS_START 3 +#define BASS_FILEPOS_CONNECTED 4 +#define BASS_FILEPOS_BUFFER 5 +#define BASS_FILEPOS_SOCKET 6 + +typedef void (CALLBACK DOWNLOADPROC)(const void *buffer, DWORD length, void *user); +/* Internet stream download callback function. +buffer : Buffer containing the downloaded data... NULL=end of download +length : Number of bytes in the buffer +user : The 'user' parameter value given when calling BASS_StreamCreateURL */ + +// BASS_ChannelSetSync types +#define BASS_SYNC_POS 0 +#define BASS_SYNC_END 2 +#define BASS_SYNC_META 4 +#define BASS_SYNC_SLIDE 5 +#define BASS_SYNC_STALL 6 +#define BASS_SYNC_DOWNLOAD 7 +#define BASS_SYNC_FREE 8 +#define BASS_SYNC_SETPOS 11 +#define BASS_SYNC_MUSICPOS 10 +#define BASS_SYNC_MUSICINST 1 +#define BASS_SYNC_MUSICFX 3 +#define BASS_SYNC_OGG_CHANGE 12 +#define BASS_SYNC_MIXTIME 0x40000000 // FLAG: sync at mixtime, else at playtime +#define BASS_SYNC_ONETIME 0x80000000 // FLAG: sync only once, else continuously + +typedef void (CALLBACK SYNCPROC)(HSYNC handle, DWORD channel, DWORD data, void *user); +/* Sync callback function. NOTE: a sync callback function should be very +quick as other syncs can't be processed until it has finished. If the sync +is a "mixtime" sync, then other streams and MOD musics can't be mixed until +it's finished either. +handle : The sync that has occured +channel: Channel that the sync occured in +data : Additional data associated with the sync's occurance +user : The 'user' parameter given when calling BASS_ChannelSetSync */ + +typedef void (CALLBACK DSPPROC)(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user); +/* DSP callback function. NOTE: A DSP function should obviously be as quick as +possible... other DSP functions, streams and MOD musics can not be processed +until it's finished. +handle : The DSP handle +channel: Channel that the DSP is being applied to +buffer : Buffer to apply the DSP to +length : Number of bytes in the buffer +user : The 'user' parameter given when calling BASS_ChannelSetDSP */ + +typedef BOOL (CALLBACK RECORDPROC)(HRECORD handle, const void *buffer, DWORD length, void *user); +/* Recording callback function. +handle : The recording handle +buffer : Buffer containing the recorded sample data +length : Number of bytes +user : The 'user' parameter value given when calling BASS_RecordStart +RETURN : TRUE = continue recording, FALSE = stop */ + +// BASS_ChannelIsActive return values +#define BASS_ACTIVE_STOPPED 0 +#define BASS_ACTIVE_PLAYING 1 +#define BASS_ACTIVE_STALLED 2 +#define BASS_ACTIVE_PAUSED 3 + +// Channel attributes +#define BASS_ATTRIB_FREQ 1 +#define BASS_ATTRIB_VOL 2 +#define BASS_ATTRIB_PAN 3 +#define BASS_ATTRIB_EAXMIX 4 +#define BASS_ATTRIB_NOBUFFER 5 +#define BASS_ATTRIB_CPU 7 +#define BASS_ATTRIB_SRC 8 +#define BASS_ATTRIB_MUSIC_AMPLIFY 0x100 +#define BASS_ATTRIB_MUSIC_PANSEP 0x101 +#define BASS_ATTRIB_MUSIC_PSCALER 0x102 +#define BASS_ATTRIB_MUSIC_BPM 0x103 +#define BASS_ATTRIB_MUSIC_SPEED 0x104 +#define BASS_ATTRIB_MUSIC_VOL_GLOBAL 0x105 +#define BASS_ATTRIB_MUSIC_VOL_CHAN 0x200 // + channel # +#define BASS_ATTRIB_MUSIC_VOL_INST 0x300 // + instrument # + +// BASS_ChannelGetData flags +#define BASS_DATA_AVAILABLE 0 // query how much data is buffered +#define BASS_DATA_FLOAT 0x40000000 // flag: return floating-point sample data +#define BASS_DATA_FFT256 0x80000000 // 256 sample FFT +#define BASS_DATA_FFT512 0x80000001 // 512 FFT +#define BASS_DATA_FFT1024 0x80000002 // 1024 FFT +#define BASS_DATA_FFT2048 0x80000003 // 2048 FFT +#define BASS_DATA_FFT4096 0x80000004 // 4096 FFT +#define BASS_DATA_FFT8192 0x80000005 // 8192 FFT +#define BASS_DATA_FFT16384 0x80000006 // 16384 FFT +#define BASS_DATA_FFT_INDIVIDUAL 0x10 // FFT flag: FFT for each channel, else all combined +#define BASS_DATA_FFT_NOWINDOW 0x20 // FFT flag: no Hanning window +#define BASS_DATA_FFT_REMOVEDC 0x40 // FFT flag: pre-remove DC bias +#define BASS_DATA_FFT_COMPLEX 0x80 // FFT flag: return complex data + +// BASS_ChannelGetTags types : what's returned +#define BASS_TAG_ID3 0 // ID3v1 tags : TAG_ID3 structure +#define BASS_TAG_ID3V2 1 // ID3v2 tags : variable length block +#define BASS_TAG_OGG 2 // OGG comments : series of null-terminated UTF-8 strings +#define BASS_TAG_HTTP 3 // HTTP headers : series of null-terminated ANSI strings +#define BASS_TAG_ICY 4 // ICY headers : series of null-terminated ANSI strings +#define BASS_TAG_META 5 // ICY metadata : ANSI string +#define BASS_TAG_APE 6 // APE tags : series of null-terminated UTF-8 strings +#define BASS_TAG_MP4 7 // MP4/iTunes metadata : series of null-terminated UTF-8 strings +#define BASS_TAG_VENDOR 9 // OGG encoder : UTF-8 string +#define BASS_TAG_LYRICS3 10 // Lyric3v2 tag : ASCII string +#define BASS_TAG_CA_CODEC 11 // CoreAudio codec info : TAG_CA_CODEC structure +#define BASS_TAG_MF 13 // Media Foundation tags : series of null-terminated UTF-8 strings +#define BASS_TAG_WAVEFORMAT 14 // WAVE format : WAVEFORMATEEX structure +#define BASS_TAG_RIFF_INFO 0x100 // RIFF "INFO" tags : series of null-terminated ANSI strings +#define BASS_TAG_RIFF_BEXT 0x101 // RIFF/BWF "bext" tags : TAG_BEXT structure +#define BASS_TAG_RIFF_CART 0x102 // RIFF/BWF "cart" tags : TAG_CART structure +#define BASS_TAG_RIFF_DISP 0x103 // RIFF "DISP" text tag : ANSI string +#define BASS_TAG_APE_BINARY 0x1000 // + index #, binary APE tag : TAG_APE_BINARY structure +#define BASS_TAG_MUSIC_NAME 0x10000 // MOD music name : ANSI string +#define BASS_TAG_MUSIC_MESSAGE 0x10001 // MOD message : ANSI string +#define BASS_TAG_MUSIC_ORDERS 0x10002 // MOD order list : BYTE array of pattern numbers +#define BASS_TAG_MUSIC_INST 0x10100 // + instrument #, MOD instrument name : ANSI string +#define BASS_TAG_MUSIC_SAMPLE 0x10300 // + sample #, MOD sample name : ANSI string + +// ID3v1 tag structure +typedef struct { + char id[3]; + char title[30]; + char artist[30]; + char album[30]; + char year[4]; + char comment[30]; + BYTE genre; +} TAG_ID3; + +// Binary APE tag structure +typedef struct { + const char *key; + const void *data; + DWORD length; +} TAG_APE_BINARY; + +// BWF "bext" tag structure +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4200) +#endif +#pragma pack(push,1) +typedef struct { + char Description[256]; // description + char Originator[32]; // name of the originator + char OriginatorReference[32]; // reference of the originator + char OriginationDate[10]; // date of creation (yyyy-mm-dd) + char OriginationTime[8]; // time of creation (hh-mm-ss) + QWORD TimeReference; // first sample count since midnight (little-endian) + WORD Version; // BWF version (little-endian) + BYTE UMID[64]; // SMPTE UMID + BYTE Reserved[190]; +#if defined(__GNUC__) && __GNUC__<3 + char CodingHistory[0]; // history +#elif 1 // change to 0 if compiler fails the following line + char CodingHistory[]; // history +#else + char CodingHistory[1]; // history +#endif +} TAG_BEXT; +#pragma pack(pop) + +// BWF "cart" tag structures +typedef struct +{ + DWORD dwUsage; // FOURCC timer usage ID + DWORD dwValue; // timer value in samples from head +} TAG_CART_TIMER; + +typedef struct +{ + char Version[4]; // version of the data structure + char Title[64]; // title of cart audio sequence + char Artist[64]; // artist or creator name + char CutID[64]; // cut number identification + char ClientID[64]; // client identification + char Category[64]; // category ID, PSA, NEWS, etc + char Classification[64]; // classification or auxiliary key + char OutCue[64]; // out cue text + char StartDate[10]; // yyyy-mm-dd + char StartTime[8]; // hh:mm:ss + char EndDate[10]; // yyyy-mm-dd + char EndTime[8]; // hh:mm:ss + char ProducerAppID[64]; // name of vendor or application + char ProducerAppVersion[64]; // version of producer application + char UserDef[64]; // user defined text + DWORD dwLevelReference; // sample value for 0 dB reference + TAG_CART_TIMER PostTimer[8]; // 8 time markers after head + char Reserved[276]; + char URL[1024]; // uniform resource locator +#if defined(__GNUC__) && __GNUC__<3 + char TagText[0]; // free form text for scripts or tags +#elif 1 // change to 0 if compiler fails the following line + char TagText[]; // free form text for scripts or tags +#else + char TagText[1]; // free form text for scripts or tags +#endif +} TAG_CART; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// CoreAudio codec info structure +typedef struct { + DWORD ftype; // file format + DWORD atype; // audio format + const char *name; // description +} TAG_CA_CODEC; + +#ifndef _WAVEFORMATEX_ +#define _WAVEFORMATEX_ +#pragma pack(push,1) +typedef struct tWAVEFORMATEX +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; +} WAVEFORMATEX, *PWAVEFORMATEX, *LPWAVEFORMATEX; +typedef const WAVEFORMATEX *LPCWAVEFORMATEX; +#pragma pack(pop) +#endif + +// BASS_ChannelGetLength/GetPosition/SetPosition modes +#define BASS_POS_BYTE 0 // byte position +#define BASS_POS_MUSIC_ORDER 1 // order.row position, MAKELONG(order,row) +#define BASS_POS_OGG 3 // OGG bitstream number +#define BASS_POS_DECODE 0x10000000 // flag: get the decoding (not playing) position +#define BASS_POS_DECODETO 0x20000000 // flag: decode to the position instead of seeking + +// BASS_RecordSetInput flags +#define BASS_INPUT_OFF 0x10000 +#define BASS_INPUT_ON 0x20000 + +#define BASS_INPUT_TYPE_MASK 0xff000000 +#define BASS_INPUT_TYPE_UNDEF 0x00000000 +#define BASS_INPUT_TYPE_DIGITAL 0x01000000 +#define BASS_INPUT_TYPE_LINE 0x02000000 +#define BASS_INPUT_TYPE_MIC 0x03000000 +#define BASS_INPUT_TYPE_SYNTH 0x04000000 +#define BASS_INPUT_TYPE_CD 0x05000000 +#define BASS_INPUT_TYPE_PHONE 0x06000000 +#define BASS_INPUT_TYPE_SPEAKER 0x07000000 +#define BASS_INPUT_TYPE_WAVE 0x08000000 +#define BASS_INPUT_TYPE_AUX 0x09000000 +#define BASS_INPUT_TYPE_ANALOG 0x0a000000 + +// DX8 effect types, use with BASS_ChannelSetFX +enum +{ + BASS_FX_DX8_CHORUS, + BASS_FX_DX8_COMPRESSOR, + BASS_FX_DX8_DISTORTION, + BASS_FX_DX8_ECHO, + BASS_FX_DX8_FLANGER, + BASS_FX_DX8_GARGLE, + BASS_FX_DX8_I3DL2REVERB, + BASS_FX_DX8_PARAMEQ, + BASS_FX_DX8_REVERB +}; + +typedef struct { + float fWetDryMix; + float fDepth; + float fFeedback; + float fFrequency; + DWORD lWaveform; // 0=triangle, 1=sine + float fDelay; + DWORD lPhase; // BASS_DX8_PHASE_xxx +} BASS_DX8_CHORUS; + +typedef struct { + float fGain; + float fAttack; + float fRelease; + float fThreshold; + float fRatio; + float fPredelay; +} BASS_DX8_COMPRESSOR; + +typedef struct { + float fGain; + float fEdge; + float fPostEQCenterFrequency; + float fPostEQBandwidth; + float fPreLowpassCutoff; +} BASS_DX8_DISTORTION; + +typedef struct { + float fWetDryMix; + float fFeedback; + float fLeftDelay; + float fRightDelay; + BOOL lPanDelay; +} BASS_DX8_ECHO; + +typedef struct { + float fWetDryMix; + float fDepth; + float fFeedback; + float fFrequency; + DWORD lWaveform; // 0=triangle, 1=sine + float fDelay; + DWORD lPhase; // BASS_DX8_PHASE_xxx +} BASS_DX8_FLANGER; + +typedef struct { + DWORD dwRateHz; // Rate of modulation in hz + DWORD dwWaveShape; // 0=triangle, 1=square +} BASS_DX8_GARGLE; + +typedef struct { + int lRoom; // [-10000, 0] default: -1000 mB + int lRoomHF; // [-10000, 0] default: 0 mB + float flRoomRolloffFactor; // [0.0, 10.0] default: 0.0 + float flDecayTime; // [0.1, 20.0] default: 1.49s + float flDecayHFRatio; // [0.1, 2.0] default: 0.83 + int lReflections; // [-10000, 1000] default: -2602 mB + float flReflectionsDelay; // [0.0, 0.3] default: 0.007 s + int lReverb; // [-10000, 2000] default: 200 mB + float flReverbDelay; // [0.0, 0.1] default: 0.011 s + float flDiffusion; // [0.0, 100.0] default: 100.0 % + float flDensity; // [0.0, 100.0] default: 100.0 % + float flHFReference; // [20.0, 20000.0] default: 5000.0 Hz +} BASS_DX8_I3DL2REVERB; + +typedef struct { + float fCenter; + float fBandwidth; + float fGain; +} BASS_DX8_PARAMEQ; + +typedef struct { + float fInGain; // [-96.0,0.0] default: 0.0 dB + float fReverbMix; // [-96.0,0.0] default: 0.0 db + float fReverbTime; // [0.001,3000.0] default: 1000.0 ms + float fHighFreqRTRatio; // [0.001,0.999] default: 0.001 +} BASS_DX8_REVERB; + +#define BASS_DX8_PHASE_NEG_180 0 +#define BASS_DX8_PHASE_NEG_90 1 +#define BASS_DX8_PHASE_ZERO 2 +#define BASS_DX8_PHASE_90 3 +#define BASS_DX8_PHASE_180 4 + +typedef void (CALLBACK IOSNOTIFYPROC)(DWORD status); +/* iOS notification callback function. +status : The notification (BASS_IOSNOTIFY_xxx) */ + +#define BASS_IOSNOTIFY_INTERRUPT 1 // interruption started +#define BASS_IOSNOTIFY_INTERRUPT_END 2 // interruption ended + +BOOL BASSDEF(BASS_SetConfig)(DWORD option, DWORD value); +DWORD BASSDEF(BASS_GetConfig)(DWORD option); +BOOL BASSDEF(BASS_SetConfigPtr)(DWORD option, const void *value); +void *BASSDEF(BASS_GetConfigPtr)(DWORD option); +DWORD BASSDEF(BASS_GetVersion)(); +int BASSDEF(BASS_ErrorGetCode)(); +BOOL BASSDEF(BASS_GetDeviceInfo)(DWORD device, BASS_DEVICEINFO *info); +#if defined(_WIN32) && !defined(_WIN32_WCE) +BOOL BASSDEF(BASS_Init)(int device, DWORD freq, DWORD flags, HWND win, const GUID *dsguid); +#else +BOOL BASSDEF(BASS_Init)(int device, DWORD freq, DWORD flags, void *win, void *dsguid); +#endif +BOOL BASSDEF(BASS_SetDevice)(DWORD device); +DWORD BASSDEF(BASS_GetDevice)(); +BOOL BASSDEF(BASS_Free)(); +#if defined(_WIN32) && !defined(_WIN32_WCE) +void *BASSDEF(BASS_GetDSoundObject)(DWORD object); +#endif +BOOL BASSDEF(BASS_GetInfo)(BASS_INFO *info); +BOOL BASSDEF(BASS_Update)(DWORD length); +float BASSDEF(BASS_GetCPU)(); +BOOL BASSDEF(BASS_Start)(); +BOOL BASSDEF(BASS_Stop)(); +BOOL BASSDEF(BASS_Pause)(); +BOOL BASSDEF(BASS_SetVolume)(float volume); +float BASSDEF(BASS_GetVolume)(); + +HPLUGIN BASSDEF(BASS_PluginLoad)(const char *file, DWORD flags); +BOOL BASSDEF(BASS_PluginFree)(HPLUGIN handle); +const BASS_PLUGININFO *BASSDEF(BASS_PluginGetInfo)(HPLUGIN handle); + +BOOL BASSDEF(BASS_Set3DFactors)(float distf, float rollf, float doppf); +BOOL BASSDEF(BASS_Get3DFactors)(float *distf, float *rollf, float *doppf); +BOOL BASSDEF(BASS_Set3DPosition)(const BASS_3DVECTOR *pos, const BASS_3DVECTOR *vel, const BASS_3DVECTOR *front, const BASS_3DVECTOR *top); +BOOL BASSDEF(BASS_Get3DPosition)(BASS_3DVECTOR *pos, BASS_3DVECTOR *vel, BASS_3DVECTOR *front, BASS_3DVECTOR *top); +void BASSDEF(BASS_Apply3D)(); +#if defined(_WIN32) && !defined(_WIN32_WCE) +BOOL BASSDEF(BASS_SetEAXParameters)(int env, float vol, float decay, float damp); +BOOL BASSDEF(BASS_GetEAXParameters)(DWORD *env, float *vol, float *decay, float *damp); +#endif + +HMUSIC BASSDEF(BASS_MusicLoad)(BOOL mem, const void *file, QWORD offset, DWORD length, DWORD flags, DWORD freq); +BOOL BASSDEF(BASS_MusicFree)(HMUSIC handle); + +HSAMPLE BASSDEF(BASS_SampleLoad)(BOOL mem, const void *file, QWORD offset, DWORD length, DWORD max, DWORD flags); +HSAMPLE BASSDEF(BASS_SampleCreate)(DWORD length, DWORD freq, DWORD chans, DWORD max, DWORD flags); +BOOL BASSDEF(BASS_SampleFree)(HSAMPLE handle); +BOOL BASSDEF(BASS_SampleSetData)(HSAMPLE handle, const void *buffer); +BOOL BASSDEF(BASS_SampleGetData)(HSAMPLE handle, void *buffer); +BOOL BASSDEF(BASS_SampleGetInfo)(HSAMPLE handle, BASS_SAMPLE *info); +BOOL BASSDEF(BASS_SampleSetInfo)(HSAMPLE handle, const BASS_SAMPLE *info); +HCHANNEL BASSDEF(BASS_SampleGetChannel)(HSAMPLE handle, BOOL onlynew); +DWORD BASSDEF(BASS_SampleGetChannels)(HSAMPLE handle, HCHANNEL *channels); +BOOL BASSDEF(BASS_SampleStop)(HSAMPLE handle); + +HSTREAM BASSDEF(BASS_StreamCreate)(DWORD freq, DWORD chans, DWORD flags, STREAMPROC *proc, void *user); +HSTREAM BASSDEF(BASS_StreamCreateFile)(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags); +HSTREAM BASSDEF(BASS_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user); +HSTREAM BASSDEF(BASS_StreamCreateFileUser)(DWORD system, DWORD flags, const BASS_FILEPROCS *proc, void *user); +BOOL BASSDEF(BASS_StreamFree)(HSTREAM handle); +QWORD BASSDEF(BASS_StreamGetFilePosition)(HSTREAM handle, DWORD mode); +DWORD BASSDEF(BASS_StreamPutData)(HSTREAM handle, const void *buffer, DWORD length); +DWORD BASSDEF(BASS_StreamPutFileData)(HSTREAM handle, const void *buffer, DWORD length); + +BOOL BASSDEF(BASS_RecordGetDeviceInfo)(DWORD device, BASS_DEVICEINFO *info); +BOOL BASSDEF(BASS_RecordInit)(int device); +BOOL BASSDEF(BASS_RecordSetDevice)(DWORD device); +DWORD BASSDEF(BASS_RecordGetDevice)(); +BOOL BASSDEF(BASS_RecordFree)(); +BOOL BASSDEF(BASS_RecordGetInfo)(BASS_RECORDINFO *info); +const char *BASSDEF(BASS_RecordGetInputName)(int input); +BOOL BASSDEF(BASS_RecordSetInput)(int input, DWORD flags, float volume); +DWORD BASSDEF(BASS_RecordGetInput)(int input, float *volume); +HRECORD BASSDEF(BASS_RecordStart)(DWORD freq, DWORD chans, DWORD flags, RECORDPROC *proc, void *user); + +double BASSDEF(BASS_ChannelBytes2Seconds)(DWORD handle, QWORD pos); +QWORD BASSDEF(BASS_ChannelSeconds2Bytes)(DWORD handle, double pos); +DWORD BASSDEF(BASS_ChannelGetDevice)(DWORD handle); +BOOL BASSDEF(BASS_ChannelSetDevice)(DWORD handle, DWORD device); +DWORD BASSDEF(BASS_ChannelIsActive)(DWORD handle); +BOOL BASSDEF(BASS_ChannelGetInfo)(DWORD handle, BASS_CHANNELINFO *info); +const char *BASSDEF(BASS_ChannelGetTags)(DWORD handle, DWORD tags); +DWORD BASSDEF(BASS_ChannelFlags)(DWORD handle, DWORD flags, DWORD mask); +BOOL BASSDEF(BASS_ChannelUpdate)(DWORD handle, DWORD length); +BOOL BASSDEF(BASS_ChannelLock)(DWORD handle, BOOL lock); +BOOL BASSDEF(BASS_ChannelPlay)(DWORD handle, BOOL restart); +BOOL BASSDEF(BASS_ChannelStop)(DWORD handle); +BOOL BASSDEF(BASS_ChannelPause)(DWORD handle); +BOOL BASSDEF(BASS_ChannelSetAttribute)(DWORD handle, DWORD attrib, float value); +BOOL BASSDEF(BASS_ChannelGetAttribute)(DWORD handle, DWORD attrib, float *value); +BOOL BASSDEF(BASS_ChannelSlideAttribute)(DWORD handle, DWORD attrib, float value, DWORD time); +BOOL BASSDEF(BASS_ChannelIsSliding)(DWORD handle, DWORD attrib); +BOOL BASSDEF(BASS_ChannelSet3DAttributes)(DWORD handle, int mode, float min, float max, int iangle, int oangle, float outvol); +BOOL BASSDEF(BASS_ChannelGet3DAttributes)(DWORD handle, DWORD *mode, float *min, float *max, DWORD *iangle, DWORD *oangle, float *outvol); +BOOL BASSDEF(BASS_ChannelSet3DPosition)(DWORD handle, const BASS_3DVECTOR *pos, const BASS_3DVECTOR *orient, const BASS_3DVECTOR *vel); +BOOL BASSDEF(BASS_ChannelGet3DPosition)(DWORD handle, BASS_3DVECTOR *pos, BASS_3DVECTOR *orient, BASS_3DVECTOR *vel); +QWORD BASSDEF(BASS_ChannelGetLength)(DWORD handle, DWORD mode); +BOOL BASSDEF(BASS_ChannelSetPosition)(DWORD handle, QWORD pos, DWORD mode); +QWORD BASSDEF(BASS_ChannelGetPosition)(DWORD handle, DWORD mode); +DWORD BASSDEF(BASS_ChannelGetLevel)(DWORD handle); +DWORD BASSDEF(BASS_ChannelGetData)(DWORD handle, void *buffer, DWORD length); +HSYNC BASSDEF(BASS_ChannelSetSync)(DWORD handle, DWORD type, QWORD param, SYNCPROC *proc, void *user); +BOOL BASSDEF(BASS_ChannelRemoveSync)(DWORD handle, HSYNC sync); +HDSP BASSDEF(BASS_ChannelSetDSP)(DWORD handle, DSPPROC *proc, void *user, int priority); +BOOL BASSDEF(BASS_ChannelRemoveDSP)(DWORD handle, HDSP dsp); +BOOL BASSDEF(BASS_ChannelSetLink)(DWORD handle, DWORD chan); +BOOL BASSDEF(BASS_ChannelRemoveLink)(DWORD handle, DWORD chan); +HFX BASSDEF(BASS_ChannelSetFX)(DWORD handle, DWORD type, int priority); +BOOL BASSDEF(BASS_ChannelRemoveFX)(DWORD handle, HFX fx); + +BOOL BASSDEF(BASS_FXSetParameters)(HFX handle, const void *params); +BOOL BASSDEF(BASS_FXGetParameters)(HFX handle, void *params); +BOOL BASSDEF(BASS_FXReset)(HFX handle); + +#ifdef __cplusplus +} + +#ifdef _WIN32 +static inline HPLUGIN BASS_PluginLoad(const WCHAR *file, DWORD flags) +{ + return BASS_PluginLoad((const char*)file, flags|BASS_UNICODE); +} + +static inline HMUSIC BASS_MusicLoad(BOOL mem, const WCHAR *file, QWORD offset, DWORD length, DWORD flags, DWORD freq) +{ + return BASS_MusicLoad(mem, (const void*)file, offset, length, flags|BASS_UNICODE, freq); +} + +static inline HSAMPLE BASS_SampleLoad(BOOL mem, const WCHAR *file, QWORD offset, DWORD length, DWORD max, DWORD flags) +{ + return BASS_SampleLoad(mem, (const void*)file, offset, length, max, flags|BASS_UNICODE); +} + +static inline HSTREAM BASS_StreamCreateFile(BOOL mem, const WCHAR *file, QWORD offset, QWORD length, DWORD flags) +{ + return BASS_StreamCreateFile(mem, (const void*)file, offset, length, flags|BASS_UNICODE); +} + +static inline HSTREAM BASS_StreamCreateURL(const WCHAR *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user) +{ + return BASS_StreamCreateURL((const char*)url, offset, flags|BASS_UNICODE, proc, user); +} +#endif +#endif + +#endif diff --git a/tetris_ai/bass.lib b/tetris_ai/bass.lib new file mode 100644 index 0000000..ac5ab1d Binary files /dev/null and b/tetris_ai/bass.lib differ diff --git a/tetris_ai/bin/bass.dll b/tetris_ai/bin/bass.dll new file mode 100644 index 0000000..2dcea83 Binary files /dev/null and b/tetris_ai/bin/bass.dll differ diff --git a/tetris_ai/bin/misamino.ini b/tetris_ai/bin/misamino.ini new file mode 100644 index 0000000..60bb480 --- /dev/null +++ b/tetris_ai/bin/misamino.ini @@ -0,0 +1,38 @@ +[AI] +delay=20 +move=5 +4w=0 + +[AI_P1] +style=0 +level=4 +PieceMul=1 +dllplugin=plugin/tojai/tojai.dll + +[AI_P2] +style=2 +level=4 +PieceMul=1 +dllplugin=plugin/dllai.dll + +[Rule] +turnbase=1 +KOS_turnbase=0 +spin180=0 +GarbageStyle=0 +GarbageCancel=1 +GarbageBuffer=1 +GarbageBlocking=1 +samesequence=1 +combo_table_style=1 + +[Player] +das=8 +softdropdas=3 +softdropdelay=10 + +[Sound] +p1sfx=1 +p2sfx=0 +bgm=1 + diff --git a/tetris_ai/bin/plugin/tojai/lua5.1.dll b/tetris_ai/bin/plugin/tojai/lua5.1.dll new file mode 100644 index 0000000..b87f3b6 Binary files /dev/null and b/tetris_ai/bin/plugin/tojai/lua5.1.dll differ diff --git a/tetris_ai/bin/plugin/tojai/setting.txt b/tetris_ai/bin/plugin/tojai/setting.txt new file mode 100644 index 0000000..c89af5c --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/setting.txt @@ -0,0 +1 @@ +toj_tetris \ No newline at end of file diff --git a/tetris_ai/bin/plugin/tojai/toj_aihelp.lua b/tetris_ai/bin/plugin/tojai/toj_aihelp.lua new file mode 100644 index 0000000..2752caa --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/toj_aihelp.lua @@ -0,0 +1,193 @@ +function Start() + SetThinkDepth(0) ; +-- SetHoldMinDiff(100) ; +-- SetThinkDelayFrame(10) ; +-- SetMoveDelayFrame(10) ; +-- + SetHoldMinDiff(0) ; + SetThinkDelayFrame(0) ; + SetMoveDelayFrame(3) ; +end + + +function End() +end + +function PrepareThink() + local height = GetBlockHighest() - (GetHeight() - GetBlockLastHeight()) ; +end + +function EvalField(depth) + + --int x,y,i,full,empty,btype,well,blocked,lblock,rblock; + -- Intermediate Results + local HorizontalTransitions = 0;--°¡·Î·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local FilledLines = 0; + local HighestBlock = -1; + local VerticalTransitions = 0;--À§·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local BlockedCells = 0;--±¸¸ÛÀÇ À§¸¦ ¸·ÀºÁ¤µµ + local Wells = 0; --¾çÂÊ¿¡ ºí·°ÀÌ ÀÖÀ»¶§ ºí·°ÀÇ ±íÀÌ ÆÄÀÎÁ¤µµ + local TetrisShape = 0 ; + local StartScore = 0 ; + local BlockedWells = 0 ; --±¸¸ÛÀ» ¸·Àº ºí·°ÀÇ ³ôÀÌ + + local efield = {} ; + local x,y,i,full,empty,btype,well,blocked,lblock,rblock; + local width, height, startHeight ; + + width = GetWidth() ; + --height = GetHeight() ; + height = GetBlockLastHeight() ; + startHeight = GetHeight()-GetBlockHighest() ; + + HighestBlock = height ; + + --Debug(string.format('HighestBlock(%d)',HighestBlock)) ; + + -- Copy to eField, Calculate HorizontalTransitions, FilledLines and HighestBlock + y=height-1 ; + i=y ; + while(y>=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x 0 and FilledLines < 3) then + --Debug(string.format('Score : %d, FillLine : %d, height : %d, blockCells : %d', FinalScore, FilledLines, HighestBlock, BlockedCells)) ; + -- end + --end + +-- Debug(string.format('Score(%d), StartScore(%d), HighestBlock(%d), HorizontalTransitions(%d), VerticalTransitions(%d), BlockedCells(%d), Wells(%d), FilledLine(%d), BlockedWells(%d)', +-- FinalScore, +-- StartScore, +-- HighestBlock, +-- HorizontalTransitions, +-- VerticalTransitions, +-- BlockedCells, +-- Wells, +-- FilledLines, +-- BlockedWells +-- )) ; + + + return FinalScore; + +end diff --git a/tetris_ai/bin/plugin/tojai/toj_combo.lua b/tetris_ai/bin/plugin/tojai/toj_combo.lua new file mode 100644 index 0000000..9fa4420 --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/toj_combo.lua @@ -0,0 +1,299 @@ +local THINK_STATE_COMBO_BUILD = 0 ; +local THINK_STATE_BUSY = 1 ; +local curThinkState_ = THINK_STATE_COMBO_BUILD ; +local COMBO_BUILD_MAX_HEIGHT = 12 ; +local WANT_EMPTY = 3 ; +local COMBO_BUILD_MIN_EVAL = -5000 ; + +function Start() + SetThinkDepth(0) ; +-- SetHoldMinDiff(100) ; +-- SetThinkDelayFrame(10) ; +-- SetMoveDelayFrame(10) ; +-- + SetHoldMinDiff(0) ; + SetThinkDelayFrame(0) ; + SetMoveDelayFrame(0) ; +end + +function End() +end + +function PrepareThink() + local height = GetBlockHighest() - (GetHeight() - GetBlockLastHeight()) ; + + curThinkState_ = THINK_STATE_COMBO_BUILD ; + + local curEval = EvalField() ; + + if(height >= COMBO_BUILD_MAX_HEIGHT or curEval < COMBO_BUILD_MIN_EVAL) then --Æò°¡ Á¡¼ö°¡ 5000ÀÌÇÏ¸é ¾ø¿¡°Ô ¼³Á¤ + curThinkState_ = THINK_STATE_BUSY ; + end + + + --Debug(string.format('PrepareThink : state(%d), height(%d), curEval(%d)', curThinkState_, height, curEval)) ; + --curThinkState_ = THINK_STATE_BUSY ; +end + +function EvalField(depth) + + --int x,y,i,full,empty,btype,well,blocked,lblock,rblock; + -- Intermediate Results + local HorizontalTransitions = 0;--°¡·Î·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local FilledLines = 0; + local HighestBlock = -1; + local VerticalTransitions = 0;--À§·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local BlockedCells = 0;--±¸¸ÛÀÇ À§¸¦ ¸·ÀºÁ¤µµ + local Wells = 0; --¾çÂÊ¿¡ ºí·°ÀÌ ÀÖÀ»¶§ ºí·°ÀÇ ±íÀÌ ÆÄÀÎÁ¤µµ + local TetrisShape = 0 ; + local StartScore = 0 ; + local BlockedWells = 0 ; --±¸¸ÛÀ» ¸·Àº ºí·°ÀÇ ³ôÀÌ + + local efield = {} ; + local x,y,i,full,empty,btype,well,blocked,lblock,rblock; + local width, height, startHeight ; + + width = GetWidth() ; + --height = GetHeight() ; + height = GetBlockLastHeight() ; + startHeight = GetHeight()-GetBlockHighest() ; + + HighestBlock = height ; + + -- Copy to eField, Calculate HorizontalTransitions, FilledLines and HighestBlock + y=height-1 ; + i=y ; + while(y>=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x= 0 and rpos >= width) then --ºí·°ÀÌ ¾ø´Ù°¡ ´Ù½Ã »ý°Ü³­ ÁöÁ¡ + rpos = j ; + end + end + j = j+1 ; + end + +-- Debug(string.format('shape : %d(empty:%d, lpos(%d), rpos(%d), x(%d))(%d%d%d%d%d%d%d%d%d%d)', +-- i, +-- emptyCnt, +-- lpos, +-- rpos, +-- x, +-- efield[0+i*width], +-- efield[1+i*width], +-- efield[2+i*width], +-- efield[3+i*width], +-- efield[4+i*width], +-- efield[5+i*width], +-- efield[6+i*width], +-- efield[7+i*width], +-- efield[8+i*width], +-- efield[9+i*width] +-- )) ; + + if(emptyCnt == WANT_EMPTY and rpos - lpos == WANT_EMPTY and lpos == x) then + TetrisShape = TetrisShape + 1 ; +-- Debug(string.format('tetris shape : %d(empty:%d, lpos(%d), rpos(%d))(%d%d%d%d%d%d%d%d%d%d)', +-- i, +-- emptyCnt, +-- lpos, +-- rpos, +-- efield[0+i*width], +-- efield[1+i*width], +-- efield[2+i*width], +-- efield[3+i*width], +-- efield[4+i*width], +-- efield[5+i*width], +-- efield[6+i*width], +-- efield[7+i*width], +-- efield[8+i*width], +-- efield[9+i*width] +-- )) ; + else + break ;--¼¼´Â °úÁ¤À» ÁßÁö + end + i = i + 1 ; + end + end + + + + if(lblock == 1 and rblock == 1) then --Á¿쿡 ºí·°ÀÌ ÀÖÀ»¶§ + i=y ; + while(i 0) then + if(comboCnt >= 5) then --5ÄÞº¸ ÀÌ»óÀÌ¸é ´õ °¡ÁßÄ¡¸¦ ÁØ´Ù. + StartScore = StartScore + 5000 ; + elseif(comboCnt >= 1) then --1ÄÞº¸ ÀÌ»óÀÌ¸é °¡ÁßÄ¡ + StartScore = StartScore + 2000 ; + else --ÄÞº¸°¡ ¾Æ´Ï¸é ÀÚÁ¦ÇÏ°Ô.. + StartScore = StartScore - 1000 ; + end + end + + + FinalScore = + StartScore + + (-10 * HighestBlock)+ + (-10 * HorizontalTransitions)+ + (-10 * VerticalTransitions)+ + (-1000 * BlockedCells)+ + (-9 * Wells)+ + --( 1 * FilledLines)+ + (100 * TetrisShape)+ + (-10 * BlockedWells) ; + elseif(curThinkState_ == THINK_STATE_BUSY) then + + if(FilledLines > 0) then + if(comboCnt >= 5) then --5ÄÞº¸ ÀÌ»óÀÌ¸é ´õ °¡ÁßÄ¡¸¦ ÁØ´Ù. + StartScore = StartScore + 5000 ; + elseif(comboCnt >= 1) then --1ÄÞº¸ ÀÌ»óÀÌ¸é °¡ÁßÄ¡ + StartScore = StartScore + 2000 ; + end + end + + FinalScore = + StartScore + + (-100 * HighestBlock) + +(-100 * HorizontalTransitions) + +(-100 * VerticalTransitions) + +(-10 * BlockedCells) + +(-9 * Wells) + +(100 * FilledLines) + +(0 * TetrisShape) + +(-10 * BlockedWells) + ; + + end + + + + --if(curThinkState_ == THINK_STATE_COMBO_BUILD) then + -- if(FilledLines > 0 and FilledLines < 3) then + --Debug(string.format('Score : %d, FillLine : %d, height : %d, blockCells : %d', FinalScore, FilledLines, HighestBlock, BlockedCells)) ; + -- end + --end + +-- Debug(string.format('Score(%d), StartScore(%d), HighestBlock(%d), HorizontalTransitions(%d), VerticalTransitions(%d), BlockedCells(%d), Wells(%d), FilledLine(%d), TetrisShape(%d), BlockedWells(%d)', +-- FinalScore, +-- StartScore, +-- HighestBlock, +-- HorizontalTransitions, +-- VerticalTransitions, +-- BlockedCells, +-- Wells, +-- FilledLines, +-- TetrisShape, +-- BlockedWells +-- )) ; + + + return FinalScore; + +end diff --git a/tetris_ai/bin/plugin/tojai/toj_right.lua b/tetris_ai/bin/plugin/tojai/toj_right.lua new file mode 100644 index 0000000..9c55d30 --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/toj_right.lua @@ -0,0 +1,234 @@ +local THINK_STATE_TETRIS_BUILD = 0 ; +local THINK_STATE_BUSY = 1 ; +local TETRIS_BUILD_HEIGHT = 15 ; + +curThinkState_ = THINK_STATE_TETRIS_BUILD ; + +function Start() + SetThinkDepth(0) ; +-- SetHoldMinDiff(100) ; +-- SetThinkDelayFrame(10) ; +-- SetMoveDelayFrame(10) ; +-- + SetHoldMinDiff(0) ; + SetThinkDelayFrame(0) ; + SetMoveDelayFrame(0) ; +end + +function End() +end + +function PrepareThink() + local height = GetBlockHighest() - (GetHeight() - GetBlockLastHeight()) ; + + curThinkState_ = THINK_STATE_TETRIS_BUILD ; + + if(height >= TETRIS_BUILD_HEIGHT) then + curThinkState_ = THINK_STATE_BUSY ; + end +-- Debug(string.format('PrepareThink : state(%d), height(%d)', curThinkState_, height)) ; + --curThinkState_ = THINK_STATE_BUSY ; +end + +function EvalField(depth) + + --int x,y,i,full,empty,btype,well,blocked,lblock,rblock; + -- Intermediate Results + local HorizontalTransitions = 0;--°¡·Î·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local FilledLines = 0; + local HighestBlock = -1; + local VerticalTransitions = 0;--À§·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local BlockedCells = 0;--±¸¸ÛÀÇ À§¸¦ ¸·ÀºÁ¤µµ + local Wells = 0; --¾çÂÊ¿¡ ºí·°ÀÌ ÀÖÀ»¶§ ºí·°ÀÇ ±íÀÌ ÆÄÀÎÁ¤µµ + local TetrisShape = 0 ; + local StartScore = 0 ; + local BlockedWells = 0 ; --±¸¸ÛÀ» ¸·Àº ºí·°ÀÇ ³ôÀÌ + + local efield = {} ; + local x,y,i,full,empty,btype,well,blocked,lblock,rblock; + local width, height, startHeight ; + + width = GetWidth() ; + --height = GetHeight() ; + height = GetBlockLastHeight() ; + startHeight = GetHeight()-GetBlockHighest() ; + + HighestBlock = height ; + + -- Copy to eField, Calculate HorizontalTransitions, FilledLines and HighestBlock + y=height-1 ; + i=y ; + while(y>=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x 0 and FilledLines < 3) then + StartScore = StartScore - 1000 ; + elseif(FilledLines == 4) then + StartScore = StartScore + 1000 ; + elseif(FilledLines == 3) then + StartScore = StartScore + 1000 ; + end + FinalScore = + StartScore + + (-10 * HighestBlock)+ + (-10 * HorizontalTransitions)+ + (-10 * VerticalTransitions)+ + (-1000 * BlockedCells)+ + (-9 * Wells)+ + ( 1 * FilledLines)+ + (10 * TetrisShape)+ + (-10 * BlockedWells) ; + elseif(curThinkState_ == THINK_STATE_BUSY) then + FinalScore = + StartScore + + (-100 * HighestBlock) + +(-100 * HorizontalTransitions) + +(-100 * VerticalTransitions) + +(-10 * BlockedCells) + +(-9 * Wells) + +(100 * FilledLines) + +(0 * TetrisShape) + +(-10 * BlockedWells) + ; + + end + + + + --if(curThinkState_ == THINK_STATE_TETRIS_BUILD) then + -- if(FilledLines > 0 and FilledLines < 3) then + --Debug(string.format('Score : %d, FillLine : %d, height : %d, blockCells : %d', FinalScore, FilledLines, HighestBlock, BlockedCells)) ; + -- end + --end + +-- Debug(string.format('Score(%d), StartScore(%d), HighestBlock(%d), HorizontalTransitions(%d), VerticalTransitions(%d), BlockedCells(%d), Wells(%d), FilledLine(%d), TetrisShape(%d), BlockedWells(%d)', +-- FinalScore, +-- StartScore, +-- HighestBlock, +-- HorizontalTransitions, +-- VerticalTransitions, +-- BlockedCells, +-- Wells, +-- FilledLines, +-- TetrisShape, +-- BlockedWells +-- )) ; + + + return FinalScore; + +end diff --git a/tetris_ai/bin/plugin/tojai/toj_tetris.lua b/tetris_ai/bin/plugin/tojai/toj_tetris.lua new file mode 100644 index 0000000..042f72a --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/toj_tetris.lua @@ -0,0 +1,239 @@ +local THINK_STATE_TETRIS_BUILD = 0 ; +local THINK_STATE_BUSY = 1 ; + +curThinkState_ = THINK_STATE_TETRIS_BUILD ; + +function Start() + SetThinkDepth(0) ; +-- SetHoldMinDiff(100) ; +-- SetThinkDelayFrame(10) ; +-- SetMoveDelayFrame(10) ; +-- + SetHoldMinDiff(0) ; + SetThinkDelayFrame(0) ; + SetMoveDelayFrame(0) ; +end + + +function End() +end + +function PrepareThink() + local height = GetBlockHighest() - (GetHeight() - GetBlockLastHeight()) ; + + curThinkState_ = THINK_STATE_TETRIS_BUILD ; + + if(height >= 8) then + curThinkState_ = THINK_STATE_BUSY ; + end +-- Debug(string.format('PrepareThink : state(%d), height(%d)', curThinkState_, height)) ; + --curThinkState_ = THINK_STATE_BUSY ; +end + +function EvalField(depth) + + --int x,y,i,full,empty,btype,well,blocked,lblock,rblock; + -- Intermediate Results + local HorizontalTransitions = 0;--°¡·Î·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local FilledLines = 0; + local HighestBlock = -1; + local VerticalTransitions = 0;--À§·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local BlockedCells = 0;--±¸¸ÛÀÇ À§¸¦ ¸·ÀºÁ¤µµ + local Wells = 0; --¾çÂÊ¿¡ ºí·°ÀÌ ÀÖÀ»¶§ ºí·°ÀÇ ±íÀÌ ÆÄÀÎÁ¤µµ + local TetrisShape = 0 ; + local StartScore = 0 ; + local BlockedWells = 0 ; --±¸¸ÛÀ» ¸·Àº ºí·°ÀÇ ³ôÀÌ + + local efield = {} ; + local x,y,i,full,empty,btype,well,blocked,lblock,rblock; + local width, height, startHeight ; + + width = GetWidth() ; + --height = GetHeight() ; + height = GetBlockLastHeight() ; + startHeight = GetHeight()-GetBlockHighest() ; + + HighestBlock = height ; + + --Debug(string.format('HighestBlock(%d)',HighestBlock)) ; + + -- Copy to eField, Calculate HorizontalTransitions, FilledLines and HighestBlock + y=height-1 ; + i=y ; + while(y>=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + --Debug(string.format('HighestBlock2(%d %d %d)',i, startHeight, HighestBlock)) ; + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x 0 and FilledLines < 3) then + StartScore = StartScore - 1000 ; + elseif(FilledLines == 4) then + StartScore = StartScore + 1000 ; + elseif(FilledLines == 3) then + StartScore = StartScore + 1000 ; + end + FinalScore = + StartScore + + (-10 * HighestBlock)+ + (-10 * HorizontalTransitions)+ + (-10 * VerticalTransitions)+ + (-1000 * BlockedCells)+ + (-9 * Wells)+ + ( 1 * FilledLines)+ + (10 * TetrisShape)+ + (-10 * BlockedWells) ; + elseif(curThinkState_ == THINK_STATE_BUSY) then + FinalScore = + StartScore + + (-100 * HighestBlock) + +(-100 * HorizontalTransitions) + +(-100 * VerticalTransitions) + +(-10 * BlockedCells) + +(-9 * Wells) + +(100 * FilledLines) + +(0 * TetrisShape) + +(-10 * BlockedWells) + ; + + end + + + + --if(curThinkState_ == THINK_STATE_TETRIS_BUILD) then + -- if(FilledLines > 0 and FilledLines < 3) then + --Debug(string.format('Score : %d, FillLine : %d, height : %d, blockCells : %d', FinalScore, FilledLines, HighestBlock, BlockedCells)) ; + -- end + --end + +--[[ + Debug(string.format('Score(%d), StartScore(%d), HighestBlock(%d), HorizontalTransitions(%d), VerticalTransitions(%d), BlockedCells(%d), Wells(%d), FilledLine(%d), TetrisShape(%d), BlockedWells(%d)', + FinalScore, + StartScore, + HighestBlock, + HorizontalTransitions, + VerticalTransitions, + BlockedCells, + Wells, + FilledLines, + TetrisShape, + BlockedWells + )) ; +]]-- + + + return FinalScore; + +end diff --git a/tetris_ai/bin/plugin/tojai/toj_tetris_beginner.lua b/tetris_ai/bin/plugin/tojai/toj_tetris_beginner.lua new file mode 100644 index 0000000..adf56cb --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/toj_tetris_beginner.lua @@ -0,0 +1,236 @@ +local THINK_STATE_TETRIS_BUILD = 0 ; +local THINK_STATE_BUSY = 1 ; + +curThinkState_ = THINK_STATE_TETRIS_BUILD ; + +function Start() + SetThinkDepth(0) ; +-- SetHoldMinDiff(100) ; +-- SetThinkDelayFrame(10) ; +-- SetMoveDelayFrame(10) ; +-- + SetHoldMinDiff(10000) ; + SetThinkDelayFrame(0) ; + SetMoveDelayFrame(0) ; +end + + +function End() +end + +function PrepareThink() + local height = GetBlockHighest() - (GetHeight() - GetBlockLastHeight()) ; + + curThinkState_ = THINK_STATE_TETRIS_BUILD ; + + if(height >= 8) then + curThinkState_ = THINK_STATE_BUSY ; + end +-- Debug(string.format('PrepareThink : state(%d), height(%d)', curThinkState_, height)) ; + --curThinkState_ = THINK_STATE_BUSY ; +end + +function EvalField(depth) + + --int x,y,i,full,empty,btype,well,blocked,lblock,rblock; + -- Intermediate Results + local HorizontalTransitions = 0;--°¡·Î·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local FilledLines = 0; + local HighestBlock = -1; + local VerticalTransitions = 0;--À§·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local BlockedCells = 0;--±¸¸ÛÀÇ À§¸¦ ¸·ÀºÁ¤µµ + local Wells = 0; --¾çÂÊ¿¡ ºí·°ÀÌ ÀÖÀ»¶§ ºí·°ÀÇ ±íÀÌ ÆÄÀÎÁ¤µµ + local TetrisShape = 0 ; + local StartScore = 0 ; + local BlockedWells = 0 ; --±¸¸ÛÀ» ¸·Àº ºí·°ÀÇ ³ôÀÌ + + local efield = {} ; + local x,y,i,full,empty,btype,well,blocked,lblock,rblock; + local width, height, startHeight ; + + width = GetWidth() ; + --height = GetHeight() ; + height = GetBlockLastHeight() ; + startHeight = GetHeight()-GetBlockHighest() ; + + HighestBlock = height ; + + --Debug(string.format('HighestBlock(%d)',HighestBlock)) ; + + -- Copy to eField, Calculate HorizontalTransitions, FilledLines and HighestBlock + y=height-1 ; + i=y ; + while(y>=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x 0 and FilledLines < 3) then + StartScore = StartScore - 1000 ; + elseif(FilledLines == 4) then + StartScore = StartScore + 1000 ; + elseif(FilledLines == 3) then + StartScore = StartScore + 1000 ; + end + FinalScore = + StartScore + + (-10 * HighestBlock)+ + (-10 * HorizontalTransitions)+ + (-10 * VerticalTransitions)+ + --(-1000 * BlockedCells)+ + --(-9 * Wells)+ + ( 1 * FilledLines)+ + (10 * TetrisShape) + --+(-10 * BlockedWells) ; + elseif(curThinkState_ == THINK_STATE_BUSY) then + FinalScore = + StartScore + + (-10 * HighestBlock)+ + (-10 * HorizontalTransitions)+ + (-10 * VerticalTransitions)+ + --(-1000 * BlockedCells)+ + --(-9 * Wells)+ + (1 * FilledLines)+ + (0 * TetrisShape) + --+(-10 * BlockedWells) + ; + + end + + + + --if(curThinkState_ == THINK_STATE_TETRIS_BUILD) then + -- if(FilledLines > 0 and FilledLines < 3) then + --Debug(string.format('Score : %d, FillLine : %d, height : %d, blockCells : %d', FinalScore, FilledLines, HighestBlock, BlockedCells)) ; + -- end + --end + +-- Debug(string.format('Score(%d), StartScore(%d), HighestBlock(%d), HorizontalTransitions(%d), VerticalTransitions(%d), BlockedCells(%d), Wells(%d), FilledLine(%d), TetrisShape(%d), BlockedWells(%d)', +-- FinalScore, +-- StartScore, +-- HighestBlock, +-- HorizontalTransitions, +-- VerticalTransitions, +-- BlockedCells, +-- Wells, +-- FilledLines, +-- TetrisShape, +-- BlockedWells +-- )) ; + + + return FinalScore; + +end diff --git a/tetris_ai/bin/plugin/tojai/toj_tspin.lua b/tetris_ai/bin/plugin/tojai/toj_tspin.lua new file mode 100644 index 0000000..1057f32 --- /dev/null +++ b/tetris_ai/bin/plugin/tojai/toj_tspin.lua @@ -0,0 +1,246 @@ +local THINK_STATE_TETRIS_BUILD = 0 ; +local THINK_STATE_BUSY = 1 ; +local TRUE = 1 ; +local FALSE = 0 ; + +curThinkState_ = THINK_STATE_TETRIS_BUILD ; + +function Start() + SetThinkDepth(0) ; +-- SetHoldMinDiff(100) ; +-- SetThinkDelayFrame(10) ; +-- SetMoveDelayFrame(10) ; +-- +-- SetHoldMinDiff(0) ; +-- ÀÛÀ¸¸é ÀÛÀ»¼ö·Ï Ȧµå¸¦ ³ë¸² + SetHoldMinDiff(500) ; + SetThinkDelayFrame(0) ; + SetMoveDelayFrame(0) ; +-- Å©¸é Ŭ¼ö·Ï Ƽ½ºÇÉ À§ÁÖ·Î + SetTSpinMinDiff(1000) ; + SetEnableTSpin(1) ; +end + + +function End() +end + +function PrepareThink() + local height = GetBlockHighest() - (GetHeight() - GetBlockLastHeight()) ; + + curThinkState_ = THINK_STATE_TETRIS_BUILD ; + + if(height >= 4) then + curThinkState_ = THINK_STATE_BUSY ; + end +-- Debug(string.format('PrepareThink : state(%d), height(%d)', curThinkState_, height)) ; + --curThinkState_ = THINK_STATE_BUSY ; +end + +function EvalField(depth) + + --int x,y,i,full,empty,btype,well,blocked,lblock,rblock; + -- Intermediate Results + local HorizontalTransitions = 0;--°¡·Î·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local FilledLines = 0; + local HighestBlock = -1; + local VerticalTransitions = 0;--À§·Î 0 1 0 1 ¹Ýº¹Á¤µµ + local BlockedCells = 0;--±¸¸ÛÀÇ À§¸¦ ¸·ÀºÁ¤µµ + local Wells = 0; --¾çÂÊ¿¡ ºí·°ÀÌ ÀÖÀ»¶§ ºí·°ÀÇ ±íÀÌ ÆÄÀÎÁ¤µµ + local TetrisShape = 0 ; + local StartScore = 0 ; + local BlockedWells = 0 ; --±¸¸ÛÀ» ¸·Àº ºí·°ÀÇ ³ôÀÌ + + local efield = {} ; + local x,y,i,full,empty,btype,well,blocked,lblock,rblock; + local width, height, startHeight ; + + width = GetWidth() ; + --height = GetHeight() ; + height = GetBlockLastHeight() ; + startHeight = GetHeight()-GetBlockHighest() ; + + HighestBlock = height ; + + --Debug(string.format('HighestBlock(%d)',HighestBlock)) ; + + -- Copy to eField, Calculate HorizontalTransitions, FilledLines and HighestBlock + y=height-1 ; + i=y ; + while(y>=0) do + btype=1; full=1; empty=1; + x=width-1 ; + while (x>=0) do + if(0 == GetField(x, y)) then + efield[x+i*width]=0; + else + efield[x+i*width]=1 ; + empty = 0 ; + end + + if(btype ~= efield[x+i*width]) then + btype = efield[x+i*width]; + full=0; + HorizontalTransitions = HorizontalTransitions + 1; + end + x = x-1 ; + end + + if(0 == full) then + i = i-1; + else + FilledLines = FilledLines + 1 ; + end + + if(0 == btype) then + HorizontalTransitions = HorizontalTransitions + 1; + end + + if(i < startHeight and empty == 1) then + HighestBlock = height-(i+2); + break ; + end + + y = y-1 ; + end + + + x=0 ; + while(x 0 and FilledLines < 3) then + StartScore = StartScore - 1000 ; + elseif(FilledLines == 4) then + StartScore = StartScore + 1000 ; + elseif(FilledLines == 3) then + StartScore = StartScore + 1000 ; + end + FinalScore = + StartScore + + (-10 * HighestBlock)+ + (-10 * HorizontalTransitions)+ + (-10 * VerticalTransitions)+ + (-1000 * BlockedCells)+ + (-9 * Wells)+ + ( 1 * FilledLines)+ + (10 * TetrisShape)+ + (-10 * BlockedWells) ; + elseif(curThinkState_ == THINK_STATE_BUSY) then + FinalScore = + StartScore + + (-100 * HighestBlock) + +(-100 * HorizontalTransitions) + +(-100 * VerticalTransitions) + +(-10 * BlockedCells) + +(-9 * Wells) + +(100 * FilledLines) + +(0 * TetrisShape) + +(-10 * BlockedWells) + ; + + end + + + + --if(curThinkState_ == THINK_STATE_TETRIS_BUILD) then + -- if(FilledLines > 0 and FilledLines < 3) then + --Debug(string.format('Score : %d, FillLine : %d, height : %d, blockCells : %d', FinalScore, FilledLines, HighestBlock, BlockedCells)) ; + -- end + --end + +-- Debug(string.format('Score(%d), StartScore(%d), HighestBlock(%d), HorizontalTransitions(%d), VerticalTransitions(%d), BlockedCells(%d), Wells(%d), FilledLine(%d), TetrisShape(%d), BlockedWells(%d)', +-- FinalScore, +-- StartScore, +-- HighestBlock, +-- HorizontalTransitions, +-- VerticalTransitions, +-- BlockedCells, +-- Wells, +-- FilledLines, +-- TetrisShape, +-- BlockedWells +-- )) ; + + + return FinalScore; + +end + + + diff --git a/tetris_ai/bin/plugin/tojai/tojai.dll b/tetris_ai/bin/plugin/tojai/tojai.dll new file mode 100644 index 0000000..86f5558 Binary files /dev/null and b/tetris_ai/bin/plugin/tojai/tojai.dll differ diff --git a/tetris_ai/ege.h b/tetris_ai/ege.h new file mode 100644 index 0000000..ae68756 --- /dev/null +++ b/tetris_ai/ege.h @@ -0,0 +1,1401 @@ +/********************************************************* +* EGE (Easy Graphics Engine) +* FileName ege.h +* HomePage1 http://misakamm.github.com/xege +* HomePage2 http://misakamm.bitbucket.org/index.htm +* HomePage3 http://tcgraphics.sourceforge.net +* teiba1 http://tieba.baidu.com/f?kw=ege +* teiba2 http://tieba.baidu.com/f?kw=ege%C4%EF +* Blog: http://misakamm.com +* E-Mail: mailto:misakamm[at gmail com] +* +* FileName: ege.h +* ÔÚ VC ÏÂÄ£Äâ Borland BGI »æͼ¿â£¬ÊµÏÖ¼òµ¥µÄ»æͼ֮Ó࣬À©Õ¹Á˽ϸ´ÔӵĻæͼÄÜÁ¦ +* +* °üº¬²¢Ê¹Óñ¾¿âʱ£¬²»Òª°üº¬conio.hÍ·Îļþ +* ÕâЩͷÎļþ²»Ó¦¹²´æ£¬·ñÔò¿ÉÄÜ»á±àÒë´íÎó£¬ +* »òÕßgetch±»conio.hÄڵĸ²¸Ç£¨ÓÉ°üº¬´ÎÐò¾ö¶¨£©£¬Çë×¢Òâ +* ÈçÐè¹²´æ£¬ÇëʹÓöàÎļþ·Ö¿ª°üº¬µÄģʽʹÓ㬠+* ¼´²»ÄÜÒ»¸öcppͬʱ°üº¬£¬µ«¿ÉÒÔ·Ö¿ª°üº¬ +* ʹÓñ¾¿â£¬±ØÐëÓÃC++±àÒ룬¿ÉÖ§³ÖµÄ±àÒëÆ÷£º +* VC6/VC2008/VC2010/MinGW3.4.5/MinGW4.4.1 +*********************************************************/ + +/**************************************************************************** +** ×¢ÒâÊÂÏ +* ¡ïÈç¹ûÐèÒªÏÔʾ¿ØÖÆ̨´°¿Ú£¬ÇëÔÚ°üº¬±¾ÎļþµÄÇ°Ãæ¼ÓÒ»ÐÐdefine SHOW_CONSOLE +* ¡ïµ÷ÓÃSleepÕâ¸öAPIʱ£¬»òÕßµ÷ÓÃdelay£¬Êµ¼Ê¾ù»áת»¯Îªµ÷ÓÃdelay_ms£¬Èç±ØÐèµ÷ÓÃAPIÇëʹÓÃapi_sleep +* ¡ïdelay_ms(0)ÄÜ×ÔÐÐÅжÏÓÐûÓиüеıØÒª£¬Á¬Ðø¶à´Îµ«²»´óÁ¿µÄµ÷Óò¢²»»á²úÉúÖ¡ÂʵÄÓ°Ïì +* ¡ïµ÷ÓÃdelay_ms, delay_fps, getch, getkey, getmouse ʱ£¬´°¿ÚÄÚÈÝ¿ÉÄÜ»á¸üУ¬ÕâЩº¯ÊýÏ൱ÓÚÄÚÖÃÁËdelay_ms(0)£¬ +* Èç¹ûÄãÖ»ÐèÒª¸üд°¿Ú£¬¶ø²»ÏëµÈ´ý£¬¿ÉÒÔÓÃdelay_ms(0)¡£×¢ÒâdelayÖ»ÑÓʱ¶ø²»¸üд°¿Ú +* ¡ïºÏÀíµØʹÓÃdelay_ms/delay_fpsº¯Êý£¬¿ÉÒÔ¼õÉÙÄãµÄ³ÌÐòÕ¼ÓõÄCPU£¬·ñÔòÒ»¸ö¶¼Ã»Óе÷ÓÃͬʱҲûÓÐgetch/getmouseµÄ»°£¬³ÌÐò½«Õ¼ÂúÒ»¸öCPUµÄʱ¼ä +****************************************************************************/ + +#ifndef _EGE_H_ +#define _EGE_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif + +#ifdef _GRAPH_LIB_BUILD_ +#ifndef WINVER +#define WINVER 0x0400 // Specifies that the minimum required platform is Windows 95/NT4. +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 // Specifies that the minimum required platform is Windows 95/NT4. +#endif + +#ifndef _WIN32_WINDOWS +#define _WIN32_WINDOWS 0x0410 // Specifies that the minimum required platform is Windows 98. +#endif +#endif + +#ifndef __cplusplus +#error You must use C++ compiler, or you need filename with '.cpp' suffix +#endif + +#if defined(_INC_CONIO) || defined(_CONIO_H_) +#error can not include "conio.h" before "graphics.h" +#endif + +#if defined(_MSC_VER) +#pragma warning(disable: 4355) +#ifndef _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH +#endif +#ifndef _ALLOW_RUNTIME_LIBRARY_MISMATCH +#define _ALLOW_RUNTIME_LIBRARY_MISMATCH +#endif +#endif + +#if !defined(_GRAPH_LIB_BUILD_) && !defined(_GRAPH_NO_LIB_) + #ifdef _MSC_VER + #ifdef _WIN64 + #if (_MSC_VER >= 1700) + #if 0 && defined(_DLL) + #pragma comment(lib,"graphics1264d.lib") + #else + #pragma comment(lib,"graphics1264.lib") + #endif + #elif (_MSC_VER >= 1600) + #if 0 && defined(_DLL) + #pragma comment(lib,"graphics1064d.lib") + #else + #pragma comment(lib,"graphics1064.lib") + #endif + #elif (_MSC_VER >= 1500) + #if 0 && defined(_DLL) + #pragma comment(lib,"graphics0864d.lib") + #else + #pragma comment(lib,"graphics0864.lib") + #endif + #elif (_MSC_VER > 1200) + #pragma comment(lib,"graphics05.lib") + #else + #pragma comment(lib,"graphics.lib") + #endif + #else + #if (_MSC_VER >= 1700) + #if 0 && defined(_DLL) + #pragma comment(lib,"graphics12d.lib") + #else + #pragma comment(lib,"graphics12.lib") + #endif + #elif (_MSC_VER >= 1600) + #if 0 && defined(_DLL) + #pragma comment(lib,"graphics10d.lib") + #else + #pragma comment(lib,"graphics10.lib") + #endif + #elif (_MSC_VER >= 1500) + #if 0 && defined(_DLL) + #pragma comment(lib,"graphics08d.lib") + #else + #pragma comment(lib,"graphics08.lib") + #endif + #elif (_MSC_VER > 1200) + #pragma comment(lib,"graphics05.lib") + #else + #pragma comment(lib,"graphics.lib") + #endif + #endif + #if _MSC_VER >= 1700 + #ifdef _DEBUG + #else + #endif + #elif _MSC_VER >= 1600 + #ifdef _DEBUG + #else + #endif + #elif _MSC_VER > 1200 + #ifdef _DEBUG + //#pragma comment(linker, "/NODEFAULTLIB:MSVCRTD.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcmtd.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcpmtd.lib") + #else + #endif + #else + #ifdef _DEBUG + //#pragma comment(linker, "/NODEFAULTLIB:MSVCRTD.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcd.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcmtd.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcpmtd.lib") + #else + //#pragma comment(linker, "/NODEFAULTLIB:MSVCRT.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcmt.lib") + //#pragma comment(linker, "/NODEFAULTLIB:libcp.lib") + #endif + #endif + #endif +#endif + +#if !defined(_GRAPH_LIB_BUILD_) && !defined(_GRAPH_NO_LIB_) +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifndef _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_NON_CONFORMING_SWPRINTFS +#endif + +#ifndef _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_NON_CONFORMING_SWPRINTFS +#endif +#endif + +#include "windows.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1200 && !defined(SetWindowLongPtr) + #define SetWindowLongPtrW SetWindowLongW + #define GetWindowLongPtrW GetWindowLongW + #define GWLP_USERDATA GWL_USERDATA + #define GWLP_WNDPROC GWL_WNDPROC +#endif + +#if !defined(_GRAPH_LIB_BUILD_) && !defined(_GRAPH_NO_LIB_) +#if defined(_MSC_VER) && _MSC_VER > 1200 +//#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' ""version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif +#endif + + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif + +#ifdef _GRAPH_LIB_BUILD_ +#include +#endif + +#ifndef _Cdecl +#if __STDC__ +#define _Cdecl __cdecl +#else +#define _Cdecl __cdecl +#endif +#endif + +#ifdef _MSC_VER + #if defined(_WIN64) + #define EGEAPI + #else + #define EGEAPI _Cdecl + #endif +#else + #if defined(__WORDSIZE) + #if __WORDSIZE > 32 + #define EGEAPI + #else + #define EGEAPI _Cdecl + #endif + #else + #define EGEAPI + #endif +#endif + +#ifdef _GRAPH_LIB_BUILD_ + #define EGE_DEPRECATE(text) +#else + #ifdef _MSC_VER + #ifdef _CRT_DEPRECATE_TEXT + #define EGE_DEPRECATE(text) _CRT_DEPRECATE_TEXT("This function is deprecated, more info visit http://tcgraphics.sourceforge.net/") + #else + #define EGE_DEPRECATE(text) + #endif + #else + #define EGE_DEPRECATE(text) __attribute__((deprecated)) + #endif +#endif + +#define SHOWCONSOLE 1 // ½øÈëͼÐÎģʽʱ£¬±£Áô¿ØÖÆ̨µÄÏÔʾ +#define RGBTOBGR(color) ((((color) & 0xFF) << 16) | (((color) & 0xFF0000) >> 16) | ((color) & 0xFF00FF00)) +#define EGERGB(r, g, b) ( ((r)<<16) | ((g)<<8) | (b)) +#define EGERGBA(r, g, b, a) ( ((r)<<16) | ((g)<<8) | (b) | ((a)<<24) ) +#define EGEARGB(a, r, g, b) ( ((r)<<16) | ((g)<<8) | (b) | ((a)<<24) ) +#define EGEACOLOR(a, color) ( ((color) & 0xFFFFFF) | ((a)<<24) ) +#define EGECOLORA(color, a) ( ((color) & 0xFFFFFF) | ((a)<<24) ) +#define EGEGET_R(c) ( ((c)>>16) & 0xFF ) +#define EGEGET_G(c) ( ((c)>> 8) & 0xFF ) +#define EGEGET_B(c) ( ((c)) & 0xFF ) +#define EGEGET_A(c) ( ((c)>>24) & 0xFF ) +#define EGEGRAY(gray) ( ((gray)<<16) | ((gray)<<8) | (gray)) +#define EGEGRAYA(gray, a) ( ((gray)<<16) | ((gray)<<8) | (gray) | ((a)<<24) ) +#define EGEAGRAY(a, gray) ( ((gray)<<16) | ((gray)<<8) | (gray) | ((a)<<24) ) + +namespace ege { + +const double PI = 3.1415926535897932384626; + +enum graphics_drivers { /* define graphics drivers */ + DETECT, /* requests autodetection */ + CGA, MCGA, EGA, EGA64, EGAMONO, IBM8514,/* 1 - 6 */ + HERCMONO, ATT400, VGA, PC3270, /* 7 - 10 */ + TRUECOLOR, TRUECOLORSIZE, + CURRENT_DRIVER = -1 +}; + +enum graphics_modes { /* graphics modes for each driver */ + CGAC0 = 0, /* 320x200 palette 0; 1 page */ + CGAC1 = 1, /* 320x200 palette 1; 1 page */ + CGAC2 = 2, /* 320x200 palette 2: 1 page */ + CGAC3 = 3, /* 320x200 palette 3; 1 page */ + CGAHI = 4, /* 640x200 1 page */ + MCGAC0 = 0, /* 320x200 palette 0; 1 page */ + MCGAC1 = 1, /* 320x200 palette 1; 1 page */ + MCGAC2 = 2, /* 320x200 palette 2; 1 page */ + MCGAC3 = 3, /* 320x200 palette 3; 1 page */ + MCGAMED = 4, /* 640x200 1 page */ + MCGAHI = 5, /* 640x480 1 page */ + EGALO = 0, /* 640x200 16 color 4 pages */ + EGAHI = 1, /* 640x350 16 color 2 pages */ + EGA64LO = 0, /* 640x200 16 color 1 page */ + EGA64HI = 1, /* 640x350 4 color 1 page */ + EGAMONOHI = 0, /* 640x350 64K on card, 1 page - 256K on card, 4 pages */ + HERCMONOHI = 0, /* 720x348 2 pages */ + ATT400C0 = 0, /* 320x200 palette 0; 1 page */ + ATT400C1 = 1, /* 320x200 palette 1; 1 page */ + ATT400C2 = 2, /* 320x200 palette 2; 1 page */ + ATT400C3 = 3, /* 320x200 palette 3; 1 page */ + ATT400MED = 4, /* 640x200 1 page */ + ATT400HI = 5, /* 640x400 1 page */ + VGALO = 0, /* 640x200 16 color 4 pages */ + VGAMED = 1, /* 640x350 16 color 2 pages */ + VGAHI = 2, /* 640x480 16 color 1 page */ + PC3270HI = 0, /* 720x350 1 page */ + IBM8514LO = 0, /* 640x480 256 colors */ + IBM8514HI = 1 /*1024x768 256 colors */ +}; + +enum graphics_errors { /* graphresult error return codes */ + grOk = 0, + grNoInitGraph = -1, + grNotDetected = -2, + grFileNotFound = -3, + grInvalidDriver = -4, + grNoLoadMem = -5, + grNoScanMem = -6, + grNoFloodMem = -7, + grFontNotFound = -8, + grNoFontMem = -9, + grInvalidMode = -10, + grError = -11, /* generic error */ + grIOerror = -12, + grInvalidFont = -13, + grInvalidFontNum = -14, + grInvalidVersion = -18, + grException = 0x10, /* ege error */ + grParamError = 0x11, + grInvalidRegion = 0x12, + grOutOfMemory = 0x13, + grNullPointer = 0x14, + grAllocError = 0x15, + grInvalidMemory = 0xCDCDCDCD, +}; + +enum message_event { + MSG_EVENT_UP = 0x00, + MSG_EVENT_DOWN = 0x01, + MSG_EVENT_CLICK = 0x01, + MSG_EVENT_DBCLICK = 0x02, + MSG_EVENT_MOVE = 0x04, + MSG_EVENT_WHEEL = 0x10, +}; + +enum message_mouse { + MSG_MOUSE_LEFT = 0x01, + MSG_MOUSE_RIGHT = 0x02, + MSG_MOUSE_MID = 0x04, +}; + +// ÑÕÉ« +enum COLORS { + BLACK = 0, + BLUE = EGERGB(0, 0, 0xA8), + GREEN = EGERGB(0, 0xA8, 0), + CYAN = EGERGB(0, 0xA8, 0xA8), + RED = EGERGB(0xA8, 0, 0), + MAGENTA = EGERGB(0xA8, 0, 0xA8), + BROWN = EGERGB(0xA8, 0xA8, 0), + LIGHTGRAY = EGERGB(0xA8, 0xA8, 0xA8), + DARKGRAY = EGERGB(0x54, 0x54, 0x54), + LIGHTBLUE = EGERGB(0x54, 0x54, 0xFC), + LIGHTGREEN = EGERGB(0x54, 0xFC, 0x54), + LIGHTCYAN = EGERGB(0x54, 0xFC, 0xFC), + LIGHTRED = EGERGB(0xFC, 0x54, 0x54), + LIGHTMAGENTA = EGERGB(0xFC, 0x54, 0xFC), + YELLOW = EGERGB(0xFC, 0xFC, 0x54), + WHITE = EGERGB(0xFC, 0xFC, 0xFC), +}; + +// Ìî³äģʽ +enum fill_patterns {/* Fill patterns for get/setfillstyle */ + EMPTY_FILL, /* fills area in background color */ + SOLID_FILL, /* fills area in solid fill color */ + LINE_FILL, /* --- fill */ + LTSLASH_FILL, /* /// fill */ + SLASH_FILL, /* /// fill with thick lines */ + BKSLASH_FILL, /* \\\ fill with thick lines */ + LTBKSLASH_FILL, /* \\\ fill */ + HATCH_FILL, /* light hatch fill */ + XHATCH_FILL, /* heavy cross hatch fill */ + INTERLEAVE_FILL,/* interleaving line fill */ + WIDE_DOT_FILL, /* Widely spaced dot fill */ + CLOSE_DOT_FILL, /* Closely spaced dot fill */ + USER_FILL /* user defined fill */ +}; + +enum text_just { /* Horizontal and vertical justification + for settextjustify */ + LEFT_TEXT = 0, + CENTER_TEXT = 1, + RIGHT_TEXT = 2, + + BOTTOM_TEXT = 0, + /* CENTER_TEXT = 1, already defined above */ + TOP_TEXT = 2 +}; + +enum line_styles { /* Line styles for get/setlinestyle */ + SOLID_LINE = PS_SOLID, + CENTER_LINE = PS_DASH, + DOTTED_LINE = PS_DOT, + DASHED_LINE = PS_DASHDOT, + NULL_LINE = PS_NULL, + USERBIT_LINE = PS_USERSTYLE, /* User defined line style */ +}; + +enum key_msg_flag { + KEYMSG_CHAR_FLAG = 2, + KEYMSG_DOWN_FLAG = 1, + KEYMSG_UP_FLAG = 1, + + KEYMSG_CHAR = 0x40000, + KEYMSG_DOWN = 0x10000, + KEYMSG_UP = 0x20000, + KEYMSG_FIRSTDOWN = 0x80000, +}; + +enum music_state_flag { + MUSIC_MODE_NOT_OPEN = 0x0, + MUSIC_MODE_NOT_READY = 0x20C, + MUSIC_MODE_PAUSE = 0x211, + MUSIC_MODE_PLAY = 0x20E, + MUSIC_MODE_STOP = 0x20D, + MUSIC_MODE_OPEN = 0x212, + MUSIC_MODE_SEEK = 0x210, +}; + +enum initmode_flag { + INIT_NOBORDER = 0x1, + INIT_CHILD = 0x2, + INIT_TOPMOST = 0x4, + INIT_RENDERMANUAL = 0x8, + INIT_NOFORCEEXIT = 0x10, + INIT_WITHLOGO = 0x100, +#if defined(_DEBUG) || defined(DEBUG) + INIT_DEFAULT = 0x0, +#else + INIT_DEFAULT = INIT_WITHLOGO, +#endif + INIT_ANIMATION = INIT_DEFAULT | INIT_RENDERMANUAL | INIT_NOFORCEEXIT, +}; + +enum rendermode_e { + RENDER_AUTO, + RENDER_MANUAL, +}; + +typedef enum key_code_e { + key_mouse_l = 0x01, + key_mouse_r = 0x02, + key_mouse_m = 0x04, + key_back = 0x08, + key_tab = 0x09, + key_enter = 0x0d, + key_shift = 0x10, + key_control = 0x11, + key_menu = 0x12, + key_pause = 0x13, + key_capslock = 0x14, + key_esc = 0x1b, + key_space = 0x20, + + key_pageup = 0x21, + key_pagedown = 0x22, + key_home = 0x23, + key_end = 0x24, + + key_left = 0x25, + key_up = 0x26, + key_right = 0x27, + key_down = 0x28, + + key_print = 0x2a, + key_snapshot = 0x2c, + key_insert = 0x2d, + key_delete = 0x2e, + + key_0 = 0x30, + key_1 = 0x31, + key_2 = 0x32, + key_3 = 0x33, + key_4 = 0x34, + key_5 = 0x35, + key_6 = 0x36, + key_7 = 0x37, + key_8 = 0x38, + key_9 = 0x39, + + key_A = 0x41, + key_Z = 0x5a, + key_win_l = 0x5b, + key_win_r = 0x5c, + + key_sleep = 0x5f, + + key_num0 = 0x60, + key_num1 = 0x61, + key_num2 = 0x62, + key_num3 = 0x63, + key_num4 = 0x64, + key_num5 = 0x65, + key_num6 = 0x66, + key_num7 = 0x67, + key_num8 = 0x68, + key_num9 = 0x69, + + key_f1 = 0x70, + key_f2 = 0x71, + key_f3 = 0x72, + key_f4 = 0x73, + key_f5 = 0x74, + key_f6 = 0x75, + key_f7 = 0x76, + key_f8 = 0x77, + key_f9 = 0x78, + key_f10 = 0x79, + key_f11 = 0x7a, + key_f12 = 0x7b, + + key_numlock = 0x90, + key_scrolllock = 0x91, + + key_shift_l = 0xa0, + key_shift_r = 0xa1, + key_control_l = 0xa2, + key_control_r = 0xa3, + key_menu_l = 0xa4, + key_menu_r = 0xa5, + + key_semicolon = 0xba, + key_plus = 0xbb, + key_comma = 0xbc, + key_minus = 0xbd, + key_period = 0xbe, + key_slash = 0xbf, + key_tilde = 0xc0, + key_lbrace = 0xdb, + key_backslash = 0xdc, + key_rbrace = 0xdd, + key_quote = 0xde, + + key_ime_process = 0xe5, +}key_code_e; + +typedef enum key_msg_e { + key_msg_down = 1, + key_msg_up = 2, + key_msg_char = 4, +}key_msg_e; +typedef enum key_flag_e { + key_flag_shift = 0x100, + key_flag_ctrl = 0x200, +}key_flag_e; + +typedef enum mouse_msg_e { + mouse_msg_down = 0x10, + mouse_msg_up = 0x20, + mouse_msg_move = 0x40, + mouse_msg_wheel = 0x80, +}mouse_msg_e; +typedef enum mouse_flag_e { + mouse_flag_left = 1, + mouse_flag_right = 2, + mouse_flag_mid = 4, + mouse_flag_shift = 0x100, + mouse_flag_ctrl = 0x200, +}mouse_flag_e; + +typedef enum pattern_type_e { + pattern_none = 0, + pattern_lineargradient = 1, + pattern_pathgradient = 2, + pattern_texture = 3, +}pattern_type_e; + +typedef unsigned int color_t; + +struct viewporttype { + int left; + int top; + int right; + int bottom; + int clipflag; +}; + +struct textsettingstype { + int font; + int direction; + int charsize; + int horiz; + int vert; +}; + +struct linestyletype { + int linestyle; + unsigned short upattern; + int thickness; +}; + +typedef struct key_msg { + int key; + key_msg_e msg; + unsigned int flags; +}key_msg; + +typedef struct mouse_msg { + int x; + int y; + mouse_msg_e msg; + unsigned int flags; + int wheel; + bool is_left() {return (flags & mouse_flag_left) != 0;} + bool is_right() {return (flags & mouse_flag_right) != 0;} + bool is_mid() {return (flags & mouse_flag_mid) != 0;} + bool is_down() {return msg == mouse_msg_down;} + bool is_up() {return msg == mouse_msg_up;} + bool is_move() {return msg == mouse_msg_move;} + bool is_wheel() {return msg == mouse_msg_wheel;} +}mouse_msg; + +typedef struct ege_point { + float x; + float y; +}ege_point; + +typedef struct ege_rect { + float x; + float y; + float w; + float h; +}ege_rect; + +typedef struct ege_colpoint { + float x; + float y; + color_t color; +}ege_colpoint; + +// Êó±êÏûÏ¢ +EGE_DEPRECATE(MOUSEMSG) +struct MOUSEMSG { + UINT uMsg; // µ±Ç°Êó±êÏûÏ¢ + bool mkCtrl; // Ctrl ¼üÊÇ·ñ°´Ï + bool mkShift; // Shift ¼üÊÇ·ñ°´Ï + bool mkLButton; // Êó±ê×ó¼üÊÇ·ñ°´Ï + bool mkMButton; // Êó±êÖмüÊÇ·ñ°´Ï + bool mkRButton; // Êó±êÓÒ¼üÊÇ·ñ°´Ï + short x; // µ±Ç°Êó±ê x ×ø±ê + short y; // µ±Ç°Êó±ê y ×ø±ê + short wheel; // Êó±ê¹öÂÖ¹ö¶¯Öµ(120Ϊ»ùÊý) +}; + +struct msg_createwindow { + HANDLE hEvent; + HWND hwnd; + LPCWSTR classname; + DWORD style; + DWORD exstyle; + unsigned id; + LPVOID param; +}; + + +// »æͼ»·¾³³õʼ»¯²ÎÊý +#define INITGRAPH(x, y) struct _initgraph_{_initgraph_(){initgraph(x, y);}\ + ~_initgraph_(){closegraph();}}_g_initgraph_ +#define INITGRAPH3(x, y, f) struct _initgraph_{_initgraph_(){initgraph(x, y, f);}\ + ~_initgraph_(){closegraph();}}_g_initgraph_ + +//ÒôÀÖÀàºê +#define MUSIC_ERROR 0xFFFFFFFF + + +typedef void (CALLBACK_PROC)(); +typedef int (__stdcall MSG_KEY_PROC )(void*, unsigned, int); +typedef int (__stdcall MSG_MOUSE_PROC)(void*, unsigned, int, int, int); +typedef CALLBACK_PROC * LPCALLBACK_PROC; +typedef MSG_KEY_PROC * LPMSG_KEY_PROC; +typedef MSG_MOUSE_PROC * LPMSG_MOUSE_PROC; + +/* +×¢Ò⣺ÒÔϺ¯ÊýµÄ×¢Êͺó´ø'###'µÄº¯Êý±íʾδʵÏÖ +*/ + +struct VECTOR3D; + +// 3d ¼ÆË㸨Öúº¯Êý +void EGEAPI rotate_point3d_x(VECTOR3D * pt, float r); //»¡¶È£¬ÓÒÊÖ¶¨Ôò +void EGEAPI rotate_point3d_y(VECTOR3D * pt, float r); +void EGEAPI rotate_point3d_z(VECTOR3D * pt, float r); + +struct VECTOR3D { + float x, y, z; + VECTOR3D() { + x = 0; y = 0; z = 0; + } + VECTOR3D(float _x, float _y) { + x = _x; y = _y; z = 0; + } + VECTOR3D(float _x, float _y, float _z) { + x = _x; y = _y; z = _z; + } + VECTOR3D& operator = (const VECTOR3D& _fp) { + x = _fp.x; y = _fp.y; z = _fp.z; + return *this; + } + VECTOR3D& operator += (const VECTOR3D& _fp); + VECTOR3D& operator -= (const VECTOR3D& _fp); + VECTOR3D operator + (const VECTOR3D& _fp) const; + VECTOR3D operator - (const VECTOR3D& _fp) const; + VECTOR3D& operator *= (float f); //Ëõ·Å + VECTOR3D operator * (float f) const; //Ëõ·Å + float operator * (const VECTOR3D& _fp) const; //µã³Ë + VECTOR3D operator & (const VECTOR3D& _fp) const; //²æ³Ë + VECTOR3D& operator &= (const VECTOR3D& _fp); //²æ³Ë + float GetModule() const; + float GetSqrModule() const { + return float(x*x + y*y + z*z); + } + VECTOR3D& SetModule(float m) { + float t = m / GetModule(); + *this *= t; + return *this; + } + VECTOR3D& Rotate(float rad, const VECTOR3D& v); //ÈÆÈÎÒâÖáÐýת£¬ÓÒÊÖ¶¨Ôò£¬radΪ»¡¶È + VECTOR3D& Rotate(float rad, float x, float y, float z) { + VECTOR3D v(x, y, z); + return Rotate(rad, v); + } + VECTOR3D& Rotate(const VECTOR3D& e, const VECTOR3D& s = VECTOR3D(0.0f, 0.0f, 1.0f)); //´Ósµ½eÖ®¼äµÄ¼Ð½ÇÈ·¶¨Ðýת + static float GetAngel(const VECTOR3D& e, const VECTOR3D& s = VECTOR3D(0.0f, 0.0f, 1.0f)); +}; + + +class IMAGE; +typedef IMAGE *PIMAGE; + +// »æͼ»·¾³Ïà¹Øº¯Êý + +void EGEAPI initgraph(int Width, int Height, int Flag = INIT_DEFAULT); // ³õʼ»¯Í¼Ðλ·¾³ +void EGEAPI initgraph(int* gdriver, int* gmode, char* path); // ¼æÈÝ Borland C++ 3.1 µÄÖØÔØ£¬Ö»Ê¹Óà 640x480x24bit +void EGEAPI closegraph(); // ¹Ø±ÕͼÐλ·¾³ +bool EGEAPI is_run(); // ÅжÏUIÊÇ·ñÍ˳ö +void EGEAPI setcaption(LPCSTR caption); +void EGEAPI setcaption(LPCWSTR caption); + +void EGEAPI setrendermode(rendermode_e mode); + +// »æͼ»·¾³ÉèÖà +PIMAGE gettarget(); +int settarget(PIMAGE pbuf); // Óà NULL ÉèÖô°¿ÚΪ»æͼĿ±ê + +void EGEAPI cleardevice(PIMAGE pimg = NULL); // ÇåÆÁ + +void EGEAPI getviewport(int *pleft, int *ptop, int *pright, int *pbottom, int *pclip = 0, PIMAGE pimg = NULL); // »ñÈ¡ÊÓͼÐÅÏ¢ +void EGEAPI setviewport(int left, int top, int right, int bottom, int clip = 1, PIMAGE pimg = NULL); // ÉèÖÃÊÓͼ +void EGEAPI clearviewport(PIMAGE pimg = NULL); // Çå¿ÕÊÓͼ + +EGE_DEPRECATE(setactivepage) +void EGEAPI setactivepage(int page); // ÉèÖõ±Ç°»æͼҳ£¬¼´»æͼº¯ÊýĬÈϵÄÊä³ö»º³å£¬·¶Î§0-1£¬Ä¬ÈÏΪ0 +EGE_DEPRECATE(setvisualpage) +void EGEAPI setvisualpage(int page); // ÉèÖõ±Ç°ÏÔʾҳ£¬ÓÃÓÚÉèÖÃÏÔʾµ½´°¿ÚÉϵÄÒ³£¬·¶Î§0-1£¬Ä¬ÈÏΪ0 +EGE_DEPRECATE(swappage) +void EGEAPI swappage(); +void EGEAPI window_getviewport(struct viewporttype * viewport); +void EGEAPI window_getviewport(int* left, int* top, int* right, int* bottom); +void EGEAPI window_setviewport(int left, int top, int right, int bottom); + +// »æͼÊôÐÔ +EGE_DEPRECATE(setactivepage) +void EGEAPI getlinestyle(int *plinestyle, unsigned short *pupattern = NULL, int *pthickness = NULL, PIMAGE pimg = NULL); // »ñÈ¡µ±Ç°ÏßÐÎ +void EGEAPI setlinestyle(int linestyle, unsigned short upattern = 0, int thickness = 1, PIMAGE pimg = NULL); // ÉèÖõ±Ç°ÏßÐÎ +void EGEAPI setlinewidth(float width, PIMAGE pimg = NULL); // ÉèÖõ±Ç°Ïß¿í +//EGE_DEPRECATE(setactivepage) +//void getfillstyle(color_t *pcolor, int *ppattern = NULL, PIMAGE pimg = NULL); // »ñÈ¡Ìî³äÀàÐÍ ### +EGE_DEPRECATE(setactivepage) +void EGEAPI setfillstyle(int pattern, color_t color, PIMAGE pimg = NULL); // ÉèÖÃÌî³äÀàÐÍ + +void EGEAPI setwritemode(int mode, PIMAGE pimg = NULL); // ÉèÖûæͼλ²Ù×÷ģʽ + +//void EGEAPI graphdefaults(PIMAGE pimg = NULL); // ÖØÖÃËùÓлæͼÉèÖÃΪĬÈÏÖµ ### + +// É«²Êº¯Êý +color_t EGEAPI getcolor(PIMAGE pimg = NULL); // »ñÈ¡µ±Ç°»æͼǰ¾°É« +color_t EGEAPI getfillcolor(PIMAGE pimg = NULL); // »ñÈ¡µ±Ç°»æͼÌî³äÉ« +color_t EGEAPI getbkcolor(PIMAGE pimg = NULL); // »ñÈ¡µ±Ç°»æͼ±³¾°É« +void EGEAPI setcolor(color_t color, PIMAGE pimg = NULL); // ÉèÖõ±Ç°»æͼǰ¾°É« +void EGEAPI setfillcolor(color_t color, PIMAGE pimg = NULL); // ÉèÖõ±Ç°»æͼÌî³äÉ« +void EGEAPI setbkcolor(color_t color, PIMAGE pimg = NULL); // ÉèÖõ±Ç°»æͼ±³¾°É«£¨ÉèÖò¢×ö±³¾°É«ÏñËØÌæ»»£© +void EGEAPI setbkcolor_f(color_t color, PIMAGE pimg = NULL); // ¿ìËÙÉèÖõ±Ç°»æͼ±³¾°É«£¨Ö»ÉèÖò»»æ»­£© +void EGEAPI setfontbkcolor(color_t color, PIMAGE pimg = NULL); // ÉèÖõ±Ç°ÎÄ×Ö±³¾°É« +void EGEAPI setbkmode(int iBkMode, PIMAGE pimg = NULL); // ÉèÖñ³¾°»ìºÏģʽ(0=OPAQUE, 1=TRANSPARENT) +void EGEAPI setinitmode(int mode = INIT_DEFAULT, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT); //ÉèÖóõʼ»¯Ä£Ê½£¬mode=0ΪÆÕͨ£¬1ΪÎޱ߿ò´°¿Ú£¬xyÊdzõʼ´°¿Ú×ø±ê +int EGEAPI attachHWND(HWND hWnd); + +// ¼æÈݺê +#define RGBtoGRAY rgb2gray +#define RGBtoHSL rgb2hsl +#define RGBtoHSV rgb2hsv +#define HSLtoRGB hsl2rgb +#define HSVtoRGB hsv2rgb + +// ÑÕÉ«Ä£ÐÍת»»º¯Êý +color_t EGEAPI rgb2gray(color_t rgb); +void EGEAPI rgb2hsl(color_t rgb, float *H, float *S, float *L); +void EGEAPI rgb2hsv(color_t rgb, float *H, float *S, float *V); +color_t EGEAPI hsl2rgb(float H, float S, float L); +color_t EGEAPI hsv2rgb(float H, float S, float V); + + +// »ù±¾»æͼº¯Êý + +color_t EGEAPI getpixel (int x, int y, PIMAGE pimg = NULL); // »ñÈ¡µãµÄÑÕÉ« +void EGEAPI putpixel (int x, int y, color_t color, PIMAGE pimg = NULL); // »­µã +color_t EGEAPI getpixel_f(int x, int y, PIMAGE pimg = NULL); // »ñÈ¡µãµÄÑÕÉ« +void EGEAPI putpixel_f(int x, int y, color_t color, PIMAGE pimg = NULL); // »­µã +void EGEAPI putpixels (int nPoint, int* pPoints, PIMAGE pimg = NULL); // ÅúÁ¿»­µã +void EGEAPI putpixels_f(int nPoint, int* pPoints, PIMAGE pimg = NULL); // ÅúÁ¿»­µã + +void EGEAPI moveto(int x, int y, PIMAGE pimg = NULL); // Òƶ¯µ±Ç°µã(¾ø¶Ô×ø±ê) +void EGEAPI moverel(int dx, int dy, PIMAGE pimg = NULL); // Òƶ¯µ±Ç°µã(Ïà¶Ô×ø±ê) + +void EGEAPI line(int x1, int y1, int x2, int y2, PIMAGE pimg = NULL); // »­Ïß +void EGEAPI linerel(int dx, int dy, PIMAGE pimg = NULL); // »­Ïß(ÖÁÏà¶Ô×ø±ê) +void EGEAPI lineto(int x, int y, PIMAGE pimg = NULL); // »­Ïß(ÖÁ¾ø¶Ô×ø±ê) +void EGEAPI line_f(float x1, float y1, float x2, float y2, PIMAGE pimg = NULL); // »­Ïß +void EGEAPI linerel_f(float dx, float dy, PIMAGE pimg = NULL); // »­Ïß(ÖÁÏà¶Ô×ø±ê) +void EGEAPI lineto_f(float x, float y, PIMAGE pimg = NULL); // »­Ïß(ÖÁ¾ø¶Ô×ø±ê) + + +void EGEAPI rectangle(int left, int top, int right, int bottom, PIMAGE pimg = NULL); // »­¾ØÐÎ + +//void EGEAPI getarccoords(int *px, int *py, int *pxstart, int *pystart, int *pxend, int *pyend, PIMAGE pimg = NULL); // »ñÈ¡Ô²»¡×ø±êÐÅÏ¢ ### +void EGEAPI arc(int x, int y, int stangle, int endangle, int radius, PIMAGE pimg = NULL); // »­Ô²»¡ +void EGEAPI circle(int x, int y, int radius, PIMAGE pimg = NULL); // »­Ô² +void EGEAPI pieslice(int x, int y, int stangle, int endangle, int radius, PIMAGE pimg = NULL); // »­Ìî³äÔ²ÉÈÐÎ +void EGEAPI ellipse(int x, int y, int stangle, int endangle, int xradius, int yradius, PIMAGE pimg = NULL);// »­ÍÖÔ²»¡Ïß +void EGEAPI fillellipse(int x, int y, int xradius, int yradius, PIMAGE pimg = NULL); // »­Ìî³äÍÖÔ² +void EGEAPI sector(int x, int y, int stangle, int endangle, int xradius, int yradius, PIMAGE pimg = NULL); // »­Ìî³äÍÖÔ²ÉÈÐÎ + +void EGEAPI arcf(float x, float y, float stangle, float endangle, float radius, PIMAGE pimg = NULL); // »­Ô²»¡ +void EGEAPI circlef(float x, float y, float radius, PIMAGE pimg = NULL); // »­Ô² +void EGEAPI pieslicef(float x, float y, float stangle, float endangle, float radius, PIMAGE pimg = NULL); // »­Ìî³äÔ²ÉÈÐÎ +void EGEAPI ellipsef(float x, float y, float stangle, float endangle, float xradius, float yradius, PIMAGE pimg = NULL);// »­ÍÖÔ²»¡Ïß +void EGEAPI fillellipsef(float x, float y, float xradius, float yradius, PIMAGE pimg = NULL); // »­Ìî³äÍÖÔ² +void EGEAPI sectorf(float x, float y, float stangle, float endangle, float xradius, float yradius, PIMAGE pimg = NULL); // »­Ìî³äÍÖÔ²ÉÈÐÎ + +void EGEAPI bar(int left, int top, int right, int bottom, PIMAGE pimg = NULL); // »­Îޱ߿òÌî³ä¾ØÐÎ +void EGEAPI bar3d(int left, int top, int right, int bottom, int depth, int topflag, PIMAGE pimg = NULL); // »­Óб߿òÈýάÌî³ä¾ØÐÎ + +void EGEAPI drawpoly(int numpoints, const int *polypoints, PIMAGE pimg = NULL); // »­¶à±ßÐÎ +void EGEAPI drawlines(int numlines, const int *polypoints, PIMAGE pimg = NULL); // »­¶àÌõ²»Á¬ÐøÏߣ¨À©Õ¹º¯Êý£© +void EGEAPI drawbezier(int numpoints, const int *polypoints, PIMAGE pimg = NULL); // »­bezierÇúÏߣ¨À©Õ¹º¯Êý£© +void EGEAPI fillpoly(int numpoints, const int *polypoints, PIMAGE pimg = NULL); // »­Ìî³äµÄ¶à±ßÐÎ +void EGEAPI fillpoly_gradient(int numpoints, const ege_colpoint* polypoints, PIMAGE pimg = NULL); // »­½¥±äÌî³äµÄ¶à±ßÐÎ +void EGEAPI floodfill(int x, int y, int border, PIMAGE pimg = NULL); // °´±ß½çÑÕÉ«Ìî³äÇøÓò +void EGEAPI floodfillsurface(int x, int y, color_t areacolor, PIMAGE pimg = NULL); // °´ÆðʼµãÑÕÉ«Ìî³äÇøÓò + +// ¸ß¼¶»æͼº¯Êý£¨´øAA£© +// ege new_api +void EGEAPI ege_enable_aa(bool enable, PIMAGE pimg = NULL); + +void EGEAPI ege_line(float x1, float y1, float x2, float y2, PIMAGE pimg = NULL); +void EGEAPI ege_drawpoly(int numpoints, ege_point* polypoints, PIMAGE pimg = NULL); +void EGEAPI ege_drawcurve(int numpoints, ege_point* polypoints, PIMAGE pimg = NULL); +void EGEAPI ege_rectangle(float x, float y, float w, float h, PIMAGE pimg = NULL); +void EGEAPI ege_ellipse(float x, float y, float w, float h, PIMAGE pimg = NULL); +void EGEAPI ege_pie(float x, float y, float w, float h, float stangle, float sweepAngle, PIMAGE pimg = NULL); + +void EGEAPI ege_arc(float x, float y, float w, float h, float stangle, float sweepAngle, PIMAGE pimg = NULL); +void EGEAPI ege_bezier(int numpoints, ege_point* polypoints, PIMAGE pimg = NULL); + +void EGEAPI ege_fillpoly(int numpoints, ege_point* polypoints, PIMAGE pimg = NULL); +void EGEAPI ege_fillrect(float x, float y, float w, float h, PIMAGE pimg = NULL); +void EGEAPI ege_fillellipse(float x, float y, float w, float h, PIMAGE pimg = NULL); +void EGEAPI ege_fillpie(float x, float y, float w, float h, float stangle, float sweepAngle, PIMAGE pimg = NULL); + +void EGEAPI ege_setpattern_none(PIMAGE pimg = NULL); +void EGEAPI ege_setpattern_lineargradient(float x1, float y1, color_t c1, float x2, float y2, color_t c2, PIMAGE pimg = NULL); +void EGEAPI ege_setpattern_pathgradient(ege_point center, color_t centercolor, + int count, ege_point* points, int colcount, color_t* pointscolor, PIMAGE pimg = NULL); +void EGEAPI ege_setpattern_ellipsegradient(ege_point center, color_t centercolor, + float x, float y, float w, float h, color_t color, PIMAGE pimg = NULL); +void EGEAPI ege_setpattern_texture(PIMAGE srcimg, float x, float y, float w, float h, PIMAGE pimg = NULL); + +void EGEAPI ege_setalpha(int alpha, PIMAGE pimg = NULL); +void EGEAPI ege_gentexture(bool gen, PIMAGE pimg = NULL); +void EGEAPI ege_puttexture(PIMAGE srcimg, float x, float y, float w, float h, PIMAGE pimg = NULL); +void EGEAPI ege_puttexture(PIMAGE srcimg, ege_rect dest, PIMAGE pimg = NULL); +void EGEAPI ege_puttexture(PIMAGE srcimg, ege_rect dest, ege_rect src, PIMAGE pimg = NULL); +// + +//int EGEAPI Begin2d(); +//void EGEAPI EndRender(); + +//ʱ¼äº¯Êý£¨ÒÔϺ¯Êý²»ÄÜÔÚ¶àÏß³ÌÏÂʹÓã¬Ö»Äܸø»æͼÖ÷Ï̵߳÷Óã© +void EGEAPI ege_sleep(long ms); // ÖÁÉÙÑÓ³ÙmsºÁÃë +void EGEAPI delay(long ms); // ÖÁÉÙÑÓ³ÙmsºÁÃë +void EGEAPI delay_ms(long ms); // ƽ¾ùÑÓ³ÙmsºÁÃë +void EGEAPI delay_fps(int fps); // ƽ¾ùÑÓ³Ù1000/fpsºÁÃ룬ÓÃÓÚÎȶ¨Ö¡ÂÊ¿ØÖÆ +void EGEAPI delay_fps(long fps); // ƽ¾ùÑÓ³Ù1000/fpsºÁÃ룬ÓÃÓÚÎȶ¨Ö¡ÂÊ¿ØÖÆ +void EGEAPI delay_fps(double fps); // ƽ¾ùÑÓ³Ù1000/fpsºÁÃ룬ÓÃÓÚÎȶ¨Ö¡ÂÊ¿ØÖÆ +void EGEAPI delay_jfps(int fps); // ƽ¾ùÑÓ³Ù1000/fpsºÁÃ룬ÓÃÓÚÎȶ¨Âß¼­Ö¡ÂÊ¿ØÖÆ£¬»æͼ´øÌøÖ¡ +void EGEAPI delay_jfps(long fps); // ƽ¾ùÑÓ³Ù1000/fpsºÁÃ룬ÓÃÓÚÎȶ¨Âß¼­Ö¡ÂÊ¿ØÖÆ£¬»æͼ´øÌøÖ¡ +void EGEAPI delay_jfps(double fps); // ƽ¾ùÑÓ³Ù1000/fpsºÁÃ룬ÓÃÓÚÎȶ¨Âß¼­Ö¡ÂÊ¿ØÖÆ£¬»æͼ´øÌøÖ¡ +// ÒÔϺ¯Êý¿ÉÒÔ¶àÏß³ÌÏÂʹÓ㬷ÇͼÐÎ(worker)Ï̵߳ÄsleepʹÓÃÕâ¸ö +void EGEAPI api_sleep(long dwMilliseconds); +double EGEAPI fclock(); // »ñÈ¡ÒÔÃëΪµ¥Î»µÄ¸¡µãʱ¼ä£¬Ö»ÓÃÓÚ¼ÆʱÓ㬾«¶È0.01Ã룬 + +// ÎÄ×ÖÏà¹Øº¯Êý +void EGEAPI outtext(LPCSTR textstring, PIMAGE pimg = NULL); // ÔÚµ±Ç°Î»ÖÃÊä³öÎÄ×Ö +void EGEAPI outtext(LPCWSTR textstring, PIMAGE pimg = NULL); // ÔÚµ±Ç°Î»ÖÃÊä³öÎÄ×Ö +void EGEAPI outtext(CHAR c, PIMAGE pimg = NULL); // ÔÚµ±Ç°Î»ÖÃÊä³ö×Ö·û +void EGEAPI outtext(WCHAR c, PIMAGE pimg = NULL); // ÔÚµ±Ç°Î»ÖÃÊä³ö×Ö·û +void EGEAPI outtextxy(int x, int y, LPCSTR textstring, PIMAGE pimg = NULL); // ÔÚÖ¸¶¨Î»ÖÃÊä³öÎÄ×Ö +void EGEAPI outtextxy(int x, int y, LPCWSTR textstring, PIMAGE pimg = NULL); // ÔÚÖ¸¶¨Î»ÖÃÊä³öÎÄ×Ö +void EGEAPI outtextxy(int x, int y, CHAR c, PIMAGE pimg = NULL); // ÔÚÖ¸¶¨Î»ÖÃÊä³ö×Ö·û +void EGEAPI outtextxy(int x, int y, WCHAR c, PIMAGE pimg = NULL); // ÔÚÖ¸¶¨Î»ÖÃÊä³ö×Ö·û +void EGEAPI outtextrect(int x, int y, int w, int h, LPCSTR textstring, PIMAGE pimg = NULL); // ÔÚÖ¸¶¨¾ØÐη¶Î§Êä³öÎÄ×Ö +void EGEAPI outtextrect(int x, int y, int w, int h, LPCWSTR textstring, PIMAGE pimg = NULL); // ÔÚÖ¸¶¨¾ØÐη¶Î§Êä³öÎÄ×Ö +void EGEAPI xyprintf(int x, int y, LPCSTR fmt, ...); // ÔÚÖ¸¶¨Î»ÖÃÊä³ö¸ñʽ»¯×Ö·û´®£¬Ö¸¶¨»æͼĿ±êµ÷ÓÃsettarget +void EGEAPI xyprintf(int x, int y, LPCWSTR fmt, ...); // ÔÚÖ¸¶¨Î»ÖÃÊä³ö¸ñʽ»¯×Ö·û´®£¬Ö¸¶¨»æͼĿ±êµ÷ÓÃsettarget +void EGEAPI rectprintf(int x, int y, int w, int h, LPCSTR fmt, ...); // ÔÚÖ¸¶¨¾ØÐÎÊä³ö¸ñʽ»¯×Ö·û´®£¬Ö¸¶¨»æͼĿ±êµ÷ÓÃsettarget +void EGEAPI rectprintf(int x, int y, int w, int h, LPCWSTR fmt, ...); // ÔÚÖ¸¶¨¾ØÐÎÊä³ö¸ñʽ»¯×Ö·û´®£¬Ö¸¶¨»æͼĿ±êµ÷ÓÃsettarget +int EGEAPI textwidth(LPCSTR textstring, PIMAGE pimg = NULL); // »ñÈ¡×Ö·û´®Õ¼ÓõÄÏñËØ¿í +int EGEAPI textwidth(LPCWSTR textstring, PIMAGE pimg = NULL); // »ñÈ¡×Ö·û´®Õ¼ÓõÄÏñËØ¿í +int EGEAPI textwidth(CHAR c, PIMAGE pimg = NULL); +int EGEAPI textwidth(WCHAR c, PIMAGE pimg = NULL); +int EGEAPI textheight(LPCSTR textstring, PIMAGE pimg = NULL); // »ñÈ¡×Ö·û´®Õ¼ÓõÄÏñËØ¸ß +int EGEAPI textheight(LPCWSTR textstring, PIMAGE pimg = NULL); // »ñÈ¡×Ö·û´®Õ¼ÓõÄÏñËØ¸ß +int EGEAPI textheight(CHAR c, PIMAGE pimg = NULL); +int EGEAPI textheight(WCHAR c, PIMAGE pimg = NULL); +void EGEAPI settextjustify(int horiz, int vert, PIMAGE pimg = NULL); + +// ÉèÖõ±Ç°×ÖÌåÑùʽ(Ïê¼û°ïÖú) +// nHeight: ×Ö·ûµÄƽ¾ù¸ß¶È£» +// nWidth: ×Ö·ûµÄƽ¾ù¿í¶È(0 ±íʾ×ÔÊÊÓ¦)£» +// lpszFace: ×ÖÌåÃû³Æ£» +// nEscapement: ×Ö·û´®µÄÊéд½Ç¶È(µ¥Î» 0.1 ¶È)£» +// nOrientation: ÿ¸ö×Ö·ûµÄÊéд½Ç¶È(µ¥Î» 0.1 ¶È)£» +// nWeight: ×Ö·ûµÄ±Ê»­´Öϸ(0 ±íʾĬÈÏ´Öϸ)£» +// bItalic: ÊÇ·ñбÌ壻 +// bUnderline: ÊÇ·ñÏ»®Ïߣ» +// bStrikeOut: ÊÇ·ñɾ³ýÏߣ» +// fbCharSet: Ö¸¶¨×Ö·û¼¯£» +// fbOutPrecision: Ö¸¶¨ÎÄ×ÖµÄÊä³ö¾«¶È£» +// fbClipPrecision: Ö¸¶¨ÎÄ×ֵļô¼­¾«¶È£» +// fbQuality: Ö¸¶¨ÎÄ×ÖµÄÊä³öÖÊÁ¿£» +// fbPitchAndFamily: Ö¸¶¨ÒÔ³£¹æ·½Ê½ÃèÊö×ÖÌåµÄ×ÖÌåϵÁС£ +void EGEAPI setfont(int nHeight, int nWidth, LPCSTR lpszFace, PIMAGE pimg = NULL); +void EGEAPI setfont(int nHeight, int nWidth, LPCWSTR lpszFace, PIMAGE pimg = NULL); +void EGEAPI setfont(int nHeight, int nWidth, LPCSTR lpszFace, int nEscapement, int nOrientation, + int nWeight, int bItalic, int bUnderline, int bStrikeOut, PIMAGE pimg = NULL); +void EGEAPI setfont(int nHeight, int nWidth, LPCWSTR lpszFace, int nEscapement, int nOrientation, + int nWeight, int bItalic, int bUnderline, int bStrikeOut, PIMAGE pimg = NULL); +void EGEAPI setfont(int nHeight, int nWidth, LPCSTR lpszFace, int nEscapement, int nOrientation, + int nWeight, int bItalic, int bUnderline, int bStrikeOut, BYTE fbCharSet, + BYTE fbOutPrecision, BYTE fbClipPrecision, BYTE fbQuality, BYTE fbPitchAndFamily, PIMAGE pimg = NULL); +void EGEAPI setfont(int nHeight, int nWidth, LPCWSTR lpszFace, int nEscapement, int nOrientation, + int nWeight, int bItalic, int bUnderline, int bStrikeOut, BYTE fbCharSet, + BYTE fbOutPrecision, BYTE fbClipPrecision, BYTE fbQuality, BYTE fbPitchAndFamily, PIMAGE pimg = NULL); +void EGEAPI setfont(const LOGFONTA *font, PIMAGE pimg = NULL); // ÉèÖõ±Ç°×ÖÌåÑùʽ +void EGEAPI setfont(const LOGFONTW *font, PIMAGE pimg = NULL); // ÉèÖõ±Ç°×ÖÌåÑùʽ +void EGEAPI getfont(LOGFONTA *font, PIMAGE pimg = NULL); // »ñÈ¡µ±Ç°×ÖÌåÑùʽ +void EGEAPI getfont(LOGFONTW *font, PIMAGE pimg = NULL); // »ñÈ¡µ±Ç°×ÖÌåÑùʽ + + +//ͼƬº¯Êý +#define getmaxx getwidth +#define getmaxy getheight + +int EGEAPI getwidth(PIMAGE pimg = NULL); // »ñȡͼƬ¿í¶È +int EGEAPI getheight(PIMAGE pimg = NULL); // »ñȡͼƬ¸ß¶È +int EGEAPI getx(PIMAGE pimg = NULL); // »ñÈ¡µ±Ç° x ×ø±ê +int EGEAPI gety(PIMAGE pimg = NULL); // »ñÈ¡µ±Ç° y ×ø±ê + +PIMAGE EGEAPI newimage(); // ´´½¨PIMAGE +PIMAGE EGEAPI newimage(int width, int height); // ´´½¨PIMAGE +void EGEAPI delimage(PIMAGE pImg); // ´´½¨PIMAGE +color_t* EGEAPI getbuffer(PIMAGE pImg); + +int EGEAPI resize(PIMAGE pDstImg, int width, int height); //ÖØÉè³ß´ç +void EGEAPI getimage(PIMAGE pDstImg, int srcX, int srcY, int srcWidth, int srcHeight); // ´ÓÆÁÄ»»ñȡͼÏñ +void EGEAPI getimage(PIMAGE pDstImg, const PIMAGE pSrcImg, int srcX, int srcY, int srcWidth, int srcHeight); // ´ÓÁíÒ»¸ö PIMAGE ¶ÔÏóÖлñȡͼÏñ +int EGEAPI getimage(PIMAGE pDstImg, LPCSTR pImgFile, int zoomWidth = 0, int zoomHeight = 0); // ´ÓͼƬÎļþ»ñȡͼÏñ(bmp/jpg/gif/emf/wmf) +int EGEAPI getimage(PIMAGE pDstImg, LPCWSTR pImgFile, int zoomWidth = 0, int zoomHeight = 0); // ´ÓͼƬÎļþ»ñȡͼÏñ(bmp/jpg/gif/emf/wmf) +int EGEAPI getimage(PIMAGE pDstImg, LPCSTR pResType, LPCSTR pResName, int zoomWidth = 0, int zoomHeight = 0); // ´Ó×ÊÔ´Îļþ»ñȡͼÏñ(bmp/jpg/gif/emf/wmf) +int EGEAPI getimage(PIMAGE pDstImg, LPCWSTR pResType, LPCWSTR pResName, int zoomWidth = 0, int zoomHeight = 0); // ´Ó×ÊÔ´Îļþ»ñȡͼÏñ(bmp/jpg/gif/emf/wmf) +void EGEAPI putimage(int dstX, int dstY, const PIMAGE pSrcImg, DWORD dwRop = SRCCOPY); // »æÖÆͼÏñµ½ÆÁÄ» +void EGEAPI putimage(int dstX, int dstY, int dstWidth, int dstHeight, const PIMAGE pSrcImg, int srcX, int srcY, DWORD dwRop = SRCCOPY); // »æÖÆͼÏñµ½ÆÁÄ»(Ö¸¶¨¿í¸ß) +void EGEAPI putimage(int dstX, int dstY, int dstWidth, int dstHeight, const PIMAGE pSrcImg, int srcX, int srcY, int srcWidth, int srcHeight, DWORD dwRop = SRCCOPY); // »æÖÆͼÏñµ½ÆÁÄ»(Ö¸¶¨Ô´¿í¸ßºÍÄ¿±ê¿í¸ß½øÐÐÀ­Éì) +void EGEAPI putimage(PIMAGE pDstImg, int dstX, int dstY, const PIMAGE pSrcImg, DWORD dwRop = SRCCOPY); // »æÖÆͼÏñµ½ÁíһͼÏñÖÐ +void EGEAPI putimage(PIMAGE pDstImg, int dstX, int dstY, int dstWidth, int dstHeight, const PIMAGE pSrcImg, int srcX, int srcY, DWORD dwRop = SRCCOPY); // »æÖÆͼÏñµ½ÁíһͼÏñÖÐ(Ö¸¶¨¿í¸ß) +void EGEAPI putimage(PIMAGE pDstImg, int dstX, int dstY, int dstWidth, int dstHeight, const PIMAGE pSrcImg, int srcX, int srcY, int srcWidth, int srcHeight, DWORD dwRop = SRCCOPY); // »æÖÆͼÏñµ½ÁíһͼÏñÖÐ(Ö¸¶¨Ô´¿í¸ßºÍÄ¿±ê¿í¸ß½øÐÐÀ­Éì) +int EGEAPI saveimage(PIMAGE pimg, LPCSTR filename); +int EGEAPI saveimage(PIMAGE pimg, LPCWSTR filename); +int EGEAPI savepng(PIMAGE pimg, LPCSTR filename, int bAlpha = 0); +int EGEAPI savepng(PIMAGE pimg, LPCWSTR filename, int bAlpha = 0); +int EGEAPI getimage_pngfile(PIMAGE pimg, LPCSTR filename); +int EGEAPI getimage_pngfile(PIMAGE pimg, LPCWSTR filename); + +int EGEAPI putimage_transparent( + PIMAGE imgdest, // handle to dest + PIMAGE imgsrc, // handle to source + int nXOriginDest, // x-coord of destination upper-left corner + int nYOriginDest, // y-coord of destination upper-left corner + color_t crTransparent, // color to make transparent + int nXOriginSrc = 0, // x-coord of source upper-left corner + int nYOriginSrc = 0, // y-coord of source upper-left corner + int nWidthSrc = 0, // width of source rectangle + int nHeightSrc = 0 // height of source rectangle +); +int EGEAPI putimage_alphablend( + PIMAGE imgdest, // handle to dest + PIMAGE imgsrc, // handle to source + int nXOriginDest, // x-coord of destination upper-left corner + int nYOriginDest, // y-coord of destination upper-left corner + unsigned char alpha, // alpha + int nXOriginSrc = 0, // x-coord of source upper-left corner + int nYOriginSrc = 0, // y-coord of source upper-left corner + int nWidthSrc = 0, // width of source rectangle + int nHeightSrc = 0 // height of source rectangle +); +int EGEAPI putimage_alphatransparent( + PIMAGE imgdest, // handle to dest + PIMAGE imgsrc, // handle to source + int nXOriginDest, // x-coord of destination upper-left corner + int nYOriginDest, // y-coord of destination upper-left corner + color_t crTransparent, // color to make transparent + unsigned char alpha, // alpha + int nXOriginSrc = 0, // x-coord of source upper-left corner + int nYOriginSrc = 0, // y-coord of source upper-left corner + int nWidthSrc = 0, // width of source rectangle + int nHeightSrc = 0 // height of source rectangle +); +int EGEAPI putimage_withalpha( + PIMAGE imgdest, // handle to dest + PIMAGE imgsrc, // handle to source + int nXOriginDest, // x-coord of destination upper-left corner + int nYOriginDest, // y-coord of destination upper-left corner + int nXOriginSrc = 0, // x-coord of source upper-left corner + int nYOriginSrc = 0, // y-coord of source upper-left corner + int nWidthSrc = 0, // width of source rectangle + int nHeightSrc = 0 // height of source rectangle +); +int EGEAPI imagefilter_blurring ( + PIMAGE imgdest, // handle to dest + int intensity, + int alpha, + int nXOriginDest = 0, + int nYOriginDest = 0, + int nWidthDest = 0, + int nHeightDest = 0 +); +int EGEAPI putimage_rotate( + PIMAGE imgdest, + PIMAGE imgtexture, + int nXOriginDest, + int nYOriginDest, + float centerx, + float centery, + float radian, + int btransparent = 0, // transparent (1) or not (0) + int alpha = -1, // in range[0, 256], alpha== -1 means no alpha + int smooth = 0 +); + +int EGEAPI putimage_rotatezoom( + PIMAGE imgdest, + PIMAGE imgtexture, + int nXOriginDest, + int nYOriginDest, + float centerx, + float centery, + float radian, + float zoom, + int btransparent = 0, // transparent (1) or not (0) + int alpha = -1, // in range[0, 256], alpha== -1 means no alpha + int smooth = 0 +); + +class PushTarget { +public: + PushTarget() { m_target = gettarget(); } + PushTarget(PIMAGE target) { m_target = gettarget(); settarget(target); } + ~PushTarget() { settarget(m_target); } +private: + PIMAGE m_target; +}; + +#define CTL_PREINIT(classname, parent) struct preinit_obj { \ + preinit_obj(classname* This, int inheritlevel) { \ + This->pre_init(inheritlevel); \ + } \ + }_preinit_obj; \ + enum inherit_e { inherit_level_e = parent::inherit_level_e + 1, }; \ + static void firstinit(ege::egeControlBase* This) { \ + ((classname*)This)->m_inheritlevel = 1; \ + } \ + void pre_init(int inheritlevel) {\ + +#define CTL_PREINITEND } +#define CTL_DEFPARAM int inherit = inherit_level_e, ege::egeControlBase* pParent = NULL +#define CTL_INITBASE(parent) _preinit_obj(this, inherit_level_e), parent(inherit, (ege::egeControlBase*)pParent) +#define CTL_INIT InitObject iobj(this, inherit_level_e);\ + ege::PushTarget _pushtarget(buf()); + +#define EGECTRL_INITEND() } + +class egeControlBase +{ +public: + enum ROP { + COPY = SRCCOPY, + XOR = SRCINVERT, + AND = SRCAND, + OR = SRCPAINT, + }; + enum blendmode_e { + SOLIDCOPY = 0, + ALPHABLEND = 1, + }; + enum inherit_e { + inherit_level_e = 0, + }; + // ¹¹Ô캯Êý¿ÉÒÔ×Ô¶¨Ò壬µ«Òª°´ÐèҪѡÔñʹ²»Ê¹Óú꣬Ïê¼ûÇ°Ãæ´úÂë»òÕßÎĵµÊ¾Àý´úÂë + egeControlBase(); + egeControlBase(int inherit, egeControlBase* pParent); + ~egeControlBase(); + + // ÒÔÏÂÐ麯Êý¶¼²»ÒªÖ±½ÓÏ໥µ÷Óà + virtual LRESULT onMessage(UINT message, WPARAM wParam, LPARAM lParam) { return 0; } + // ÒÔϺ¯ÊýÈç¹û·µ»Ø·Ç0Ôò²»Ïò×ӿؼþ´«µÝ¼üÅÌÊó±êÏûÏ¢ + virtual int onMouse(int x, int y, int flag) { return 0; } + virtual int onKeyDown(int key, int flag) { return 0; } + virtual int onKeyUp(int key, int flag) { return 0; } + virtual int onKeyChar(int key, int flag) { return 0; } + // ÆÁÄ»¸üкó»á±»µ÷Óã¬ÓÃÓÚ¸üÐÂÂß¼­ + virtual int onUpdate() { return 0; } + // ÒÔÏÂGetFocusÔÚÒª»ñµÃ½¹µãʱµ÷Ó㬷µ»ØÖµÒ»°ã·µ»Ø0±íʾ»ñÈ¡¼üÅÌÊäÈë½¹µã£¬·µ»Ø·Ç0·ÅÆú»ñµÃÊäÈë½¹µã + virtual int onGetFocus() { return 0; } + // ʧȥÊäÈë½¹µãʱµ÷Óà + virtual void onLostFocus() { } + // ÉèÖóߴçÇ°µ÷Óã¬×Ô¶¨ÒåÐÞÕýº¯Êý + virtual void onSizing(int *w, int *h) {} + // ÏìÓ¦³ß´ç±ä»¯º¯Êý + virtual void onSize(int w, int h) {} + // Öػ溯Êý£¬¾¡Á¿Çë»­µ½pimgÉÏ£¬ÒÔ±ãÄÜ¿ØÖƻ滭Ŀ±ê + virtual void onDraw(PIMAGE pimg) const {} + // ³ß´ç±ä»¯Ê±µ÷Óã¬ÓÃÓÚÖØ»­¹ýÂË»º³åÇøÄÚÈÝ + virtual void onResetFilter() { + setbkcolor(BLACK, m_mainFilter); + cleardevice(m_mainFilter); + } + virtual void onAddChild(egeControlBase* pChild) {} + virtual void onDelChild(egeControlBase* pChild) {} + virtual void onIdle() {} // ±£Áô½Ó¿Ú£¬Î´Óà + // ÕâÀïÒÔÉϵĺ¯Êý¿ÉÒÔ×ÔÐж¨Ò壨עÒâÉùÃ÷ÒªÒ»Ö£¬²»ÒªÂ©µôOnDrawÀïµÄconst£© + // ÕâÀïÒÔϵÄpublicº¯Êý¿ÉÒÔµ÷Ó㬲»¿É×Ô¶¨Ò壬ÈκÎÔ¤¶¨Òå±äÁ¿¶¼²»ÒªÖ±½Ó·ÃÎÊ£¬ÇëʹÓÃÔ¤¶¨Ò庯ÊýÀ´¿ØÖÆ +public: + PIMAGE buf() { return m_mainbuf; } + PIMAGE filter() { return m_mainFilter; } + egeControlBase* parent() { return m_parent; } + PIMAGE buf() const { return m_mainbuf; } + PIMAGE filter() const { return m_mainFilter; } + const egeControlBase* parent() const { return m_parent; } + + void blendmode(int mode) { m_AlphablendMode = mode; } + void setrop(int rop) { m_rop = rop; } // ÇëÓÃö¾ÙÀàÐÍROPÀïËù¶¨ÒåµÄ + + void directdraw(bool bdraw) { m_bDirectDraw = (bdraw ? 1 : 0); } + bool isdirectdraw() const { return (m_bDirectDraw != 0); } + void autoredraw(bool bvisable) { m_bAutoDraw = (m_bAutoDraw ? 1 : 0); } + bool isautoredraw() const { return (m_bAutoDraw != 0);} + void visable(bool bvisable) { m_bVisable = (bvisable ? 1 : 0); } + bool isvisable() const { return (m_bVisable != 0);} + void enable(bool benable) { m_bEnable = (benable ? 1 : 0); } + bool isenable() const { return (m_bEnable != 0);} + void capture(bool bcapture) { m_bCapture = (bcapture ? 1 : 0); } + bool iscapture() const { return (m_bCapture != 0); } + void capmouse(bool bcapmouse) { m_bCapMouse = (bcapmouse ? 1 : 0); } + bool iscapmouse() const { return (m_bCapMouse != 0); } + bool isfocus() const { return (m_bInputFocus != 0); } + void move(int x, int y) { m_x = x; m_y = y; } + void size(int w, int h) { + onSizing(&w, &h); + m_w = w; m_h = h; + resize(m_mainbuf, w, h); + resize(m_mainFilter, w, h); + onSize(w, h); + onResetFilter(); + } + void zorderup(); + void zorderdown(); + void zorderset(int z); + + int getx() const { return m_x; } + int gety() const { return m_y; } + int getw() const { return m_w; } + int geth() const { return m_h; } + int width() const { return m_w; } + int height() const { return m_h; } + + int addchild(egeControlBase* pChild); + int delchild(egeControlBase* pChild); + void draw(PIMAGE pimg); + void update(); + void mouse(int x, int y, int flag); + void keymsgdown(unsigned key, int flag); + void keymsgup(unsigned key, int flag); + void keymsgchar(unsigned key, int flag); + bool operator < (const egeControlBase& pbase) const { + if (m_zOrderLayer != pbase.m_zOrderLayer) + return m_zOrderLayer < pbase.m_zOrderLayer; + if (m_zOrder == pbase.m_zOrder) + return this < &pbase; + else + return m_zOrder < pbase.m_zOrder; + } +protected: + int allocId(); + int allocZorder(); + class InitObject { + public: + InitObject(egeControlBase* pThis, int inherit_level); + ~InitObject(); + private: + egeControlBase* m_this; + int m_inherit_level; + }; + void (* m_preinit_func )(egeControlBase*); +private: + void init(egeControlBase* parent); + void fixzorder(); + void sortzorder(); +#if _MSC_VER <= 1200 +public: +#endif + void initok(); +private: + PIMAGE m_mainbuf; // Ö÷»º³å + PIMAGE m_mainFilter; // ¹ýÂËÆ÷ + +private: + int m_bVisable; // ÊÇ·ñ¿É¼û + int m_bEnable; // ÊÇ·ñ¿É»ñµÃÊäÈ루¼üÅ̺ÍÊó±ê£© + int m_bAutoDraw; // ÊÇ·ñ×Ô¶¯»æ»­µ½´°¿ÚÉÏ + int m_bCapture; // ÊÇ·ñ¿É»ñµÃ¼üÅÌÊäÈë½¹µã + int m_bInputFocus; // ÊÇ·ñÒѾ­»ñµÃÊäÈë½¹µã + int m_bCapMouse; // ÊÇ·ñ²¶×½Êó±ê£¨¼´Ê¹²»ÔÚËùÔÚÇøÓòÄÚ£© + int m_zOrderLayer; // Z´ÎÐò²ã£¨Öµ½Ï´óÕßÔÚÇ°£¬Öµ½ÏСÕ߻ᱻÆäËü¿Ø¼þÕÚµ²£© + int m_zOrder; // Z´ÎÐò£¨Öµ½Ï´óÕßÔÚÇ°£¬Öµ½ÏСÕ߻ᱻÆäËü¿Ø¼þÕÚµ²£© + int m_allocId; // ·ÖÅäid + int m_allocZorder; // ·ÖÅäZ´ÎÐò + + egeControlBase* m_parent; + static int s_maxchildid; // ÏÂÒ»´Î×ӿؼþ·ÖÅäIDÖµ + +#ifdef _GRAPH_LIB_BUILD_ +public: +#else +private: +#endif + void* m_childmap; // ×ӿؼþ + void* m_childzorder; // ×ӿؼþÅÅÐò + +protected: + int m_x, m_y; // ×óÉϽÇ×ø±ê + int m_w, m_h; // ¿í¸ß + +protected: + DWORD m_rop; // »ìºÏ·½Ê½ + int m_AlphablendMode; // »æ»­»ìºÏ¹ýÂË·½Ê½ + int m_bDirectDraw; // ÆôÓÃÖ±½Ó»æ»­ +#if _MSC_VER <= 1200 +public: +#endif + int m_inheritlevel; // ¼Ì³Ð²ã´Î +}; + +// ÆäËüº¯Êý + +HWND EGEAPI getHWnd(); // »ñÈ¡»æͼ´°¿Ú¾ä±ú +HINSTANCE EGEAPI getHInstance(); +PVOID EGEAPI getProcfunc(); +int EGEAPI getGraphicsVer(); // »ñÈ¡µ±Ç°°æ±¾ ### +float EGEAPI getfps(); // »ñÈ¡µ±Ç°Ö¡ÂÊ + +//Ëæ»úº¯Êý +void EGEAPI randomize(); +unsigned int EGEAPI random(unsigned int n); +double EGEAPI randomf(); + +//¸ß¼¶ÊäÈ뺯Êý +// title ¶Ô»°¿ò±êÌ⣬ text ¶Ô»°¿òÌáʾÎÄ×Ö£¬ buf½ÓÊÕÊäÈëÊý¾ÝµÄ×Ö·û´®Ö¸Õ룬 lenÖ¸³öbufµÄ×î´ó³¤¶È£¬Ò²Í¬Ê±»áÏÞÖÆÊäÈëÄÚÈݳ¤¶È +int EGEAPI inputbox_getline(LPCSTR title, LPCSTR text, LPSTR buf, int len); //µ¯³ö¶Ô»°¿ò£¬ÈÃÓû§ÊäÈ룬µ±Ç°³ÌÐòÔËÐÐÔÝÍ££¬·µ»Ø·Ç0±íʾÊäÈëÓÐЧ£¬0ΪÎÞЧ +int EGEAPI inputbox_getline(LPCWSTR title, LPCWSTR text, LPWSTR buf, int len); //µ¯³ö¶Ô»°¿ò£¬ÈÃÓû§ÊäÈ룬µ±Ç°³ÌÐòÔËÐÐÔÝÍ££¬·µ»Ø·Ç0±íʾÊäÈëÓÐЧ£¬0ΪÎÞЧ + + +//¼üÅÌ´¦Àíº¯Êý +int EGEAPI kbmsg(); +key_msg EGEAPI getkey(); +EGE_DEPRECATE(getchEx) +int EGEAPI getchEx(int flag); +EGE_DEPRECATE(kbhitEx) +int EGEAPI kbhitEx(int flag); +int EGEAPI keystate(int key); // »ñµÃ¼üÂëΪkeyµÄ¼ü£¨¼ûkey_code_e£©ÊÇ·ñ°´Ï£¬Èç¹ûkeyʹÓÃÊó±ê°´¼üµÄ¼üÂ룬Ôò»ñµÃµÄÊÇÊó±ê¼ü״̬ +void EGEAPI flushkey(); // Çå¿Õ¼üÅÌÏûÏ¢»º³åÇø + +//#ifndef _GRAPH_LIB_BUILD_ +#if !defined(_INC_CONIO) && !defined(_CONIO_H_) +#define _INC_CONIO +#define _CONIO_H_ +int EGEAPI getch(); +int EGEAPI kbhit(); +#else +#define getch getchEx +#define kbhit kbhitEx +#endif +//#endif + +//Êó±ê´¦Àíº¯Êý +int EGEAPI mousemsg(); // ¼ì²éÊÇ·ñ´æÔÚÊó±êÏûÏ¢ +mouse_msg EGEAPI getmouse(); // »ñÈ¡Ò»¸öÊó±êÏûÏ¢¡£Èç¹ûûÓУ¬¾ÍµÈ´ý +EGE_DEPRECATE(GetMouseMsg) +MOUSEMSG EGEAPI GetMouseMsg(); // £¨²»ÍƼöʹÓõĺ¯Êý£©»ñÈ¡Ò»¸öÊó±êÏûÏ¢¡£Èç¹ûûÓУ¬¾ÍµÈ´ý + +void EGEAPI flushmouse(); // Çå¿ÕÊó±êÏûÏ¢»º³åÇø +int EGEAPI showmouse(int bShow); // ÉèÖÃÊÇ·ñÏÔʾÊó±ê +int EGEAPI mousepos(int *x, int *y); // »ñÈ¡µ±Ç°Êó±êλÖà + +/* +callback function define as: +int __stdcall on_msg_key(void* param, unsigned msg, int key); +msg: see 'enum message_event' +key: keycode +return zero means process this message, otherwise means pass it and then process with 'getkey' function +*/ +//int message_addkeyhandler(void* param, LPMSG_KEY_PROC func); //ÉèÖüüÅ̻ص÷º¯Êý +/* +callback function define as: +int __stdcall on_msg_mouse(void* param, unsigned msg, int key, int x, int y); +msg: see 'enum message_event' +key: see 'enum message_mouse', if msg==MSG_EVENT_WHELL, key is a int number that indicates the distance the wheel is rotated, expressed in multiples or divisions of WHEEL_DELTA, which is 120. +x,y: current mouse (x, y) +return zero means process this message, otherwise means pass it and then process with 'GetMouseMsg' function +*/ +//int message_addmousehandler(void* param, LPMSG_MOUSE_PROC func); //ÉèÖÃÊó±ê»Øµ÷º¯Êý +int EGEAPI SetCloseHandler(LPCALLBACK_PROC func); + + +class MUSIC +{ +public: + MUSIC(); + virtual ~MUSIC(); + operator HWND()const{return (HWND)m_dwCallBack;} +public: + int IsOpen() {return (m_DID != MUSIC_ERROR) ? 1 : 0;} + DWORD OpenFile(LPCSTR filepath); + DWORD OpenFile(LPCWSTR filepath); + DWORD Play(DWORD dwFrom=MUSIC_ERROR, DWORD dwTo=MUSIC_ERROR); + DWORD Pause(); + DWORD Seek(DWORD dwTo); //²¥·ÅλÖö¨Î»£¬µ¥Î»Îªms + DWORD SetVolume(float value); + DWORD Close(); + DWORD Stop(); + DWORD GetPosition(); + DWORD GetLength(); + // ÒÔϺ¯ÊýGetPlayStatusµÄ·µ»ØֵΪÒÔÏÂÖ®Ò»£¨ÒâÒå¿´ºó׺£©£º + // MUSIC_MODE_NOT_OPEN //ûÓÐÕýÈ·´ò¿ª + // MUSIC_MODE_NOT_READY //É豸û׼±¸ºÃ £¨½ÏÉÙʹÓã© + // MUSIC_MODE_PAUSE //ÔÝÍ£ÖÐ + // MUSIC_MODE_PLAY //ÕýÔÚ²¥·Å + // MUSIC_MODE_STOP //³É¹¦´ò¿ªºó£¬»òÕß²¥·ÅÍêÊÇÕâ¸ö״̬ + // MUSIC_MODE_OPEN //´ò¿ªÖÐ £¨½ÏÉÙʹÓã© + // MUSIC_MODE_SEEK //¶¨Î»ÖÐ £¨½ÏÉÙʹÓã© + DWORD GetPlayStatus(); +private: + DWORD m_DID; + PVOID m_dwCallBack; +}; + +/* ѹËõº¯Êý */ + /* ѹËõʱdest»º³åÇøÒª±£Ö¤×îС´óСΪsourceLen * 1.001 + 16 */ + /* µ÷ÓÃcompress/compress2Ç°£¬*destLen±ØÐëÓÐÖµ£¬±íʾdest»º³åÇøµÄ×î´ó´óС£¬·µ»ØʱÕâ¸öÖµ±íʾʵ¼Ê´óС */ + /* compress2 µÄlevel ´Ó0-9£¬0²»Ñ¹Ëõ£¬9×î´óѹËõ£¬compressº¯ÊýʹÓÃĬÈÏÖµ6 */ +int EGEAPI ege_compress(void *dest, unsigned long *destLen, const void *source, unsigned long sourceLen); +int EGEAPI ege_compress2(void *dest, unsigned long *destLen, const void *source, unsigned long sourceLen, int level); +int EGEAPI ege_uncompress(void *dest, unsigned long *destLen, const void *source, unsigned long sourceLen); +unsigned long EGEAPI ege_uncompress_size(const void *source, unsigned long sourceLen); /* ·µ»Ø0±íʾ´íÎó£¬ÆäËü±íʾ´óС */ + +} // namespace ege + +#ifndef _GRAPH_LIB_BUILD_ + +#if defined(_MSC_VER) && (defined(HIDE_CONSOLE) || !defined(SHOW_CONSOLE)) +#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) +#endif + +#define Sleep(ms) delay_ms(ms) + +#endif + +#if !defined(_MSC_VER) +#define WinMain(...) WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) +#elif defined(_CONSOLE) +#if (_MSC_VER > 1200) +#define WinMain(...) main(int argc, char* argv[]) +#else +#define WinMain main +#endif +#endif + +#endif diff --git a/tetris_ai/gamemode.h b/tetris_ai/gamemode.h new file mode 100644 index 0000000..7eb5350 --- /dev/null +++ b/tetris_ai/gamemode.h @@ -0,0 +1,408 @@ +#pragma once + + +#ifndef XP_RELEASE + +std::vector genNext(std::vector val, AI::Random& rnd) { + for ( size_t i = 0; i < val.size(); ++i) { + //int i = rnd.randint(val.size()); { + int a = max(2, abs(val[i]) / 2); + a = rnd.randint(a + 1); + if ( rnd.randint(2) ) { + val[i] += a; + } else { + val[i] -= a; + } + } + return val; +} + +std::vector genNext2(std::vector val1, std::vector val2, int out, AI::Random& rnd) { + for ( size_t i = 0; i < val1.size(); ++i) { + double center = (val2[i] + val1[i]) / 2.0; + double diff = abs(val2[i] - val1[i]) / 2.0; + if ( diff < 4 ) diff = rnd.randint(5); + double a = 0, b = 0; + if ( out ) a = (rnd.randfloat() * diff * 4); + else a = (rnd.randfloat() * diff * 2); + if ( out ) b = fabs(rnd.randfloat() * val1[i] / 10); + if ( rnd.randint(2) ) a = -a, b = -b; + val1[i] = (int)floor( center + a + b + 0.5 ); + } + return val1; +} + +class EvE { + struct ai_info { + AI::AI_Param ai; + int score; + ai_info () { + memset(&ai, 0, sizeof(ai)); + score = -1; + } + ai_info( const AI::AI_Param& _ai ) : ai(_ai) { + score = -1; + } + bool operator < (const ai_info& info) const { + return score < info.score; + } + }; +public: + EvE() { + round = TRAINING_ROUND; + nround = 0; + state_find_best = false; + find_best = false; + //find_best = true; + m_init = false; + } + void init(std::deque _ai) { + ai = _ai; + state_find_best = false; + find_best = false; + //find_best = true; + m_init = false; + } + ai_info gen(ai_info param, AI::Random& rnd) { + std::vector v; + int n = sizeof(param) / sizeof(int); + for ( int i = 0; i < n; ++i ) { + v.push_back(((int*)¶m.ai)[i]); + } + v = genNext(v, rnd); + for ( int i = 0; i < n; ++i ) { + + ((int*)¶m.ai)[i] = v[i]; + } + param.score = -1; + return param; + } + ai_info gen2(ai_info param1, ai_info param2, int out, AI::Random& rnd) { + std::vector v1; + std::vector v2; + int n = sizeof(AI::AI_Param) / sizeof(int); + for ( int i = 0; i < n; ++i ) { + v1.push_back(((int*)¶m1.ai)[i]); + v2.push_back(((int*)¶m2.ai)[i]); + } + v1 = genNext2(v1, v2, out, rnd); + for ( int i = 0; i < n; ++i ) { + if ( v1[i] > 99 ) v1[i] = 99; // TODO + if ( v1[i] < -99 ) v1[i] = -99; + + ((int*)¶m1.ai)[i] = v1[i]; + } + param1.score = -1; + return param1; + } + bool loadResult(char filename[] = "param.txt") { + FILE * fp = fopen(filename, "r"); + if ( fp ) { + int n = sizeof(AI::AI_Param) / sizeof(int); + ai.clear(); + for ( size_t i = 0; ; ++i ) { + while ( i >= ai.size() ) { + ai_info p; + ai.push_back(p); + } + for ( int j = 0; j < n; ++j ) { + fscanf(fp, "%d,", ((int*)&ai[i].ai) + j); + } + fscanf(fp, "%d", &ai[i].score); + if ( feof( fp ) ) { + break; + } + } + ai.pop_back(); + fclose(fp); + nround = ai.size() / 2; + if ( ai.size() > 0 ) + return true; + else + return false; + } else { + return false; + } + } + void saveResult(char filename[] = "param.txt", bool append = false) { + FILE * fp = fopen(filename, (append ? "a": "w") ); + if ( fp ) { + int n = sizeof(AI::AI_Param) / sizeof(int); + for ( auto v: ai) { + for ( int i = 0; i < n; ++i ) { + fprintf(fp, "%4d,", ((int*)&v.ai)[i]); + } + fprintf(fp, "%4d", v.score); + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + fclose(fp); + } + } + void newgame(AI::AI_Param& p1, AI::AI_Param& p2, AI::Random& rnd) { + if ( --nround < 0 ) { + nround = ai.size() / 2; + std::random_shuffle( ai.begin(), ai.end() ); + } + /* + if ( ai.size() <= 4 ) { + saveResult("good.txt"); + while ( ai.size() < 20 ) { + int g = rnd.randint(3); + if ( g < 2 ) { + int i = rnd.randint(ai.size()-1); + ai.push_front( gen2(ai[i], ai[ai.size()-1], g, rnd) ); + } else { + ai.push_front( gen(ai[ai.size()-1], rnd) ); + } + auto it = ai.begin(); + ++it; + std::random_shuffle( it, ai.end() ); + } + } // */ + /* + for ( size_t i = 0; i < ai.size(); ++i ) { + if ( ai[0].score == -1 ) break; + ai.push_back(ai.front()); + ai.pop_front(); + } + if ( ai[0].score != -1 ) { + std::sort(ai.begin(), ai.end()); + while ( ai.size() > 5 ) ai.pop_front(); + saveResult("good.txt"); + std::vector next; + for ( size_t i = 0; i < ai.size(); ++i) { + ai[i].score = -1; + } + for ( size_t i = 0; i < ai.size(); ++i) { + for ( size_t j = i + 1; j < ai.size(); ++j) { + next.push_back( gen2(ai[i], ai[j], 0, rnd) ); + next.push_back( gen2(ai[i], ai[j], 1, rnd) ); + } + } + for ( auto v: ai) { + for ( int i = 0; i < 1; ++ i) { + next.push_back( gen(v, rnd) ); + } + } + for ( auto v: next) { + ai.push_back(v); + } + for ( size_t i = 0; i < ai.size(); ++i ) { + if ( ai[0].score == -1 ) break; + ai.push_back(ai.front()); + ai.pop_front(); + } + } + // */ + //* + volatile int ai_size = ai.size(); + while ( ai_size <= (AI_TRAINING_DEEP == 0 ? AI_TRAINING_0 : AI_TRAINING_2) ) { + if ( !find_best ) { + best_ai = ai; + ai_size = ai.size(); + } + if ( find_best && best_ai.empty() ) { + saveResult("good.txt", true); + state_find_best = true; + best_ai = ai; + } + if ( !find_best || ai.size() == 1 ) + { + saveResult("best.txt", true); + state_find_best = false; + ai = best_ai; + ai_size = best_ai.size(); + best_ai.clear(); + + std::vector next; + int ai_cur_size = ai_size; + for ( int i = 0; i < ai_cur_size; ++i) { + ai[i].score = -1; + for ( int j = i + 1; j < ai_cur_size; ++j) { + next.push_back( gen2(ai[i], ai[j], 0, rnd) ); + next.push_back( gen2(ai[i], ai[j], 1, rnd) ); + ai_size += 2; + } + } + for ( auto v: ai) { + for ( int i = 0; i < 0; ++ i) { + next.push_back( gen(v, rnd) ); + ai_size += 1; + } + } + + if ( ai.size() == 1 ) { + for ( auto v: ai) { + for ( int i = 0; i < AI_TRAINING_0; ++ i) { + next.push_back( gen(v, rnd) ); + ai_size += 1; + } + } + } + + + for ( auto v: next) { + ai.push_back(v); + } + std::random_shuffle( ai.begin(), ai.end() ); + nround = ai_size / 2; + } + if ( find_best ) break; + } + // */ + p1 = ai[0].ai; + p2 = ai[1].ai; + //p2 = ai[0].ai; + m_p1 = m_p2 = 0; + m_p1_score = 0; + m_p2_score = 0; + m_p2_sqr_score = 0; + m_p2_last_pieces = 0; + m_p2_att_val = 0; + m_p2_seed.clear(); + for ( int i = 0; i < round; ++i) { + m_p2_seed.push_back(rnd.rand()); + } + saveResult(); + } + void result(int p1_win, int p1_score, int p2_win, int p2_score) { + ai[0].score = p1_score; + ai[1].score = p2_score; + if ( !state_find_best && abs(p1_win - p2_win) < (p1_win + p2_win) / 3 ) { + ai.push_back(ai.front()); + ai.pop_front(); + ai.push_back(ai.front()); + ai.pop_front(); + } else if ( p1_win > p2_win ) { + ai.push_back(ai.front()); + ai.pop_front(); + ai.pop_front(); + } else if ( p1_win < p2_win ) { + ai.pop_front(); + ai.push_back(ai.front()); + ai.pop_front(); + } + } + void result(int score) { + ai[0].score = score; + ai.push_back(ai.front()); + ai.pop_front(); + } + void game(TetrisGame& p1, TetrisGame& p2, AI::Random& rnd) { + /* + if ( !m_init || !p2.alive() ) { + if ( m_init ) { + m_p2++; + m_p2_score += p2.total_atts; + m_p2_sqr_score += p2.total_atts * p2.total_atts; + if ( m_p2 >= round ) { + double d = 0; + d = m_p2_score / (double)m_p2; + d *= d; + d = m_p2_sqr_score / (double)m_p2 - d; + result( int(m_p2_score - sqrt(d) * m_p2) ); + newgame( p1.m_ai_param, p2.m_ai_param, rnd ); + } + } else { + newgame( p1.m_ai_param, p2.m_ai_param, rnd ); + } + p2.reset( m_p2_seed[m_p2] ); + m_init = true; + } else { + if ( p2.n_pieces != m_p2_last_pieces ) { + m_p2_last_pieces = p2.n_pieces; + if ( p2.total_atts < 7 ) { + m_p2_att_val += 20; + } else if ( p2.total_atts < 11 ) { + m_p2_att_val += 20; + } else if ( p2.total_atts < 14 ) { + m_p2_att_val += 15; + } else { + m_p2_att_val += 10; + } + //if ( std::binary_search(attmap, &attmap[23], m_p2_last_pieces) ) { + //if ( m_p2_last_pieces % 12 == 0 ) { + { + if ( p2.total_atts < 7 ) { + if ( m_p2_att_val >= 120 * 4 ) { + m_p2_att_val -= 120 * 4; + p2.acceptAttack ( 3 ); + } + } else { + if ( m_p2_att_val >= 120 * 3 ) { + m_p2_att_val -= 120 * 3; + p2.acceptAttack ( 2 ); + } + } + } + } + } + // */ + //* + if ( !m_init ) { + newgame( p1.m_ai_param, p2.m_ai_param, rnd ); + m_init = true; + } + if ( !p1.alive() || !p2.alive() ) { + if ( p1.total_clears > 0 ) { + m_p1_score += p1.total_atts * 1000 / p1.total_clears; + } + if ( p2.total_clears > 0 ) { + m_p2_score += p2.total_atts * 1000 / p2.total_clears; + } + if ( false && p1.total_atts != p2.total_atts ) { + if ( p1.total_atts > p2.total_atts ) m_p1++; + else m_p2++; + } else { + if ( p1.alive() ) m_p1++; + else m_p2++; + } + + if ( (m_p1 + m_p2) % 2 == 0) { + if ( m_p1 >= round || m_p2 >= round + || m_p1 >= 10 && (m_p1 + 1) / (m_p2 + 1) > 1 + || m_p2 >= 10 && (m_p2 + 1) / (m_p1 + 1) > 1 + || abs(m_p1 - m_p2) > round / 3 + || min(m_p1, m_p2) > round - round / 3 + ) { + result(m_p1, m_p1_score / (m_p1 + m_p2), m_p2, m_p2_score / (m_p1 + m_p2)); + newgame(p1.m_ai_param, p2.m_ai_param, rnd); + } + } + { + unsigned seed = rnd.rand(); + p1.reset(seed); + p2.reset(seed); + int pass = rnd.randint( 1024 ); + for ( int i = 0; i < pass; ++i ) { + p1.m_rand.rand(); + p2.m_rand.rand(); + } + void onGameStart(TetrisGame& tetris, AI::Random& rnd, int index); + onGameStart(p1, rnd, 0); + onGameStart(p2, rnd, 1); + } + std::swap(m_p1, m_p2); + std::swap(m_p1_score, m_p2_score); + std::swap(p1.m_ai_param, p2.m_ai_param); + std::swap(p1.n_win, p2.n_win); + } + // */ + } + volatile int state_find_best; + volatile bool find_best; + bool m_init; + std::deque ai; + std::deque best_ai; + int round; + int nround; + int m_p1, m_p2; + std::vector m_p2_seed; + int m_p1_score; + int m_p2_score; + int m_p2_sqr_score; + int m_p2_last_pieces; + int m_p2_att_val; +}; +#endif diff --git a/tetris_ai/gamepool.cpp b/tetris_ai/gamepool.cpp new file mode 100644 index 0000000..75515fd --- /dev/null +++ b/tetris_ai/gamepool.cpp @@ -0,0 +1,42 @@ +#include "gamepool.h" +#include "random.h" + +namespace AI { + uint64 hashmap[AI_POOL_MAX_H][32]; + uint64 qhashmap[AI_POOL_MAX_H][2][32]; + + struct initobj { + initobj() { + void InitHashTable(); + InitHashTable(); + } + } _init_obj; + void InitHashTable() { + Random rnd; + for ( int y = 0; y < AI_POOL_MAX_H; ++y) { + for ( int x = 0; x < 32; ++x) { + hashmap[y][x] = ((AI::uint64)rnd.rand() << 32) | rnd.rand(); + } + for ( int x = 0; x < 2; ++x) { + for ( int i = 0; i < 32; ++i) { + uint64 val = 0; + for ( int b = 0; b < 5; ++b ) { + if ( i & ( 1 << b ) ) val ^= hashmap[y][x*5+b]; + } + qhashmap[y][x][i] = val; + } + } + } + } + + uint64 hash(const GameField & pool) { + uint64 ret = 0; + for ( int y = AI_POOL_MAX_H - 1; y >= 0; --y ) { + if ( pool.m_row[y] == 0 ) break; + ret ^= qhashmap[y][0][pool.m_row[y] & 31]; + ret ^= qhashmap[y][1][(pool.m_row[y] >> 5) & 31]; + } + return ret; + } + +} \ No newline at end of file diff --git a/tetris_ai/gamepool.h b/tetris_ai/gamepool.h new file mode 100644 index 0000000..c30ee70 --- /dev/null +++ b/tetris_ai/gamepool.h @@ -0,0 +1,335 @@ +#pragma once +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + +#include +#include "tetris_gem.h" +#ifdef XP_RELEASE +#define AI_POOL_MAX_H 50 +#else +#define AI_POOL_MAX_H 32 +#endif + +namespace AI { + struct GameField; + Gem& getGem( int number, int spin ); + int getComboAttack( int combo ); + void setAllSpin(bool allSpin); + bool isEnableAllSpin(); + void setSoftdrop( bool softdrop ); + bool softdropEnable(); + typedef __int64 uint64; + void InitHashTable(); + uint64 hash(const GameField & pool); +#ifdef XP_RELEASE + const int gem_add_y = 20; +#else + const int gem_add_y = 6; +#endif + const int gem_beg_x = 3; + const int gem_beg_y = 1; + + struct GameField { + signed char m_w, m_h; + signed short combo; + signed char b2b; + unsigned long m_w_mask; + unsigned long m_row[AI_POOL_MAX_H]; + int m_hold; + int m_pc_att; + uint64 hashval; + unsigned long *row; + GameField () { + row = &m_row[gem_add_y]; + } + GameField ( const GameField& field ) { + row = &m_row[gem_add_y]; + *this = field; + } + GameField (signed char w, signed char h) { + row = &m_row[gem_add_y]; + reset(w, h); + } + int width() const { return m_w; } + int height() const { return m_h; } + void reset (signed char w, signed char h) { + m_w = w; + m_h = h; + combo = 0; + b2b = 0; + m_hold = 0; +#ifdef XP_RELEASE + m_pc_att = 6; +#else + m_pc_att = 10; +#endif + m_w_mask = ( 1 << w ) - 1; + for (int i = 0; i < AI_POOL_MAX_H; ++i) { + m_row[i] = 0; + } + for (int i = gem_add_y + m_h + 1; i < AI_POOL_MAX_H; ++i) { + m_row[i] = (unsigned)-1; + } + } + GameField& operator = (const GameField& field) { + memcpy( this, &field, (size_t)&row - (size_t)this ); + row = m_row + ( field.row - field.m_row ); + return *this; + } + bool operator == (const GameField& field) const { + if ( m_w != field.m_w || m_h != field.m_h ) return false; + if ( m_hold != field.m_hold ) return false; + if ( combo != field.combo ) return false; + if ( b2b != field.b2b ) return false; + if ( row - m_row != field.row - field.m_row ) return false; + for ( int i = 0; i <= m_h + gem_add_y; ++i ) { + if ( m_row[i] != field.m_row[i] ) return false; + } + return true; + } + //__forceinline + inline + bool isCollide(int y, const Gem & gem) const { + for ( int h = 3; h >= 0; --h ) { + if ( y + h > m_h && gem.bitmap[h] ) return true; + if ( row[y + h] & gem.bitmap[h] ) return true; + } + return false; + } + //__forceinline + inline + bool isCollide(int x, int y, const Gem & gem) const { + Gem _gem = gem; + for ( int h = 0; h < 4; ++h ) { + if ( x < 0 ) { + if (gem.bitmap[h] & ( ( 1 << (-x) ) - 1 ) ) return true; + _gem.bitmap[h] >>= -x; + } else { + if ( (gem.bitmap[h] << x) & ~m_w_mask ) return true; + _gem.bitmap[h] <<= x; + } + if ( y + h > m_h && _gem.bitmap[h] ) return true; + if ( row[y + h] & _gem.bitmap[h] ) return true; + } + return false; //isCollide(y, _gem); + } + bool wallkickTest(int& x, int& y, const Gem & gem, int spinclockwise) const { + static int Iwallkickdata[4][2][4][2] = { + { // O + { // R + { 2, 0},{-1, 0},{ 2,-1},{-1, 2}, + }, + { // L + { 1, 0},{-2, 0},{ 1, 2},{-2,-1}, + }, + }, + { // L + { // O + {-1, 0},{ 2, 0},{-1,-2},{ 2, 1}, + }, + { // 2 + { 2, 0},{-1, 0},{ 2,-1},{-1, 2}, + }, + }, + { // 2 + { // L + {-2, 0},{ 1, 0},{-2, 1},{ 1,-2}, + }, + { // R + {-1, 0},{ 2, 0},{-1,-2},{ 2, 1}, + }, + }, + { // R + { // 2 + { 1, 0},{-2, 0},{ 1, 2},{-2,-1}, + }, + { // O + {-2, 0},{ 1, 0},{-2, 1},{ 1,-2}, + }, + }, + }; + static int wallkickdata[4][2][4][2] = { + { // O + { // R + { 1, 0},{ 1, 1},{ 0,-2},{ 1,-2}, + }, + { // L + {-1, 0},{-1, 1},{ 0,-2},{-1,-2}, + }, + }, + { // L + { // O + { 1, 0},{ 1,-1},{ 0, 2},{ 1, 2}, + }, + { // 2 + { 1, 0},{ 1,-1},{ 0, 2},{ 1, 2}, + }, + }, + { // 2 + { // L + {-1, 0},{-1, 1},{ 0,-2},{-1,-2}, + }, + { // R + { 1, 0},{ 1, 1},{ 0,-2},{ 1,-2}, + }, + }, + { // R + { // 2 + {-1, 0},{-1,-1},{ 0, 2},{-1, 2}, + }, + { // O + {-1, 0},{-1,-1},{ 0, 2},{-1, 2}, + }, + }, + }; + int (*pdata)[2][4][2] = wallkickdata; + if ( gem.num == 1 ) pdata = Iwallkickdata; + for ( int itest = 0; itest < 4; ++itest) { + int dx = x + pdata[gem.spin][spinclockwise][itest][0]; + int dy = y + pdata[gem.spin][spinclockwise][itest][1]; + if ( ! isCollide(dx, dy, gem) ) { + x = dx; y = dy; + return true; + } + } + return false; + } + void paste(int x, int y, const Gem & gem) { + for ( int h = 0; h < gem.geth(); ++h ) { + if (x >= 0) + row[y + h] |= gem.bitmap[h] << x; + else + row[y + h] |= gem.bitmap[h] >> (-x); + } + } + signed char isWallKickSpin(int x, int y, const Gem & gem) const { + if ( isEnableAllSpin() ) { + if ( isCollide( x - 1, y, gem ) + && isCollide( x + 1, y, gem ) + && isCollide( x, y - 1, gem )) { + return 1; + } + } else { + if ( gem.num == 2 ) { //T + int cnt = 0; + if ( x < 0 || (row[y] & (1 << x))) ++cnt; + if ( x < 0 || y+2 > m_h || (row[y+2] & (1 << x))) ++cnt; + if ( x+2 >= m_w || (row[y] & (1 << (x+2)))) ++cnt; + if ( x+2 >= m_w || y+2 > m_h || (row[y+2] & (1 << (x+2)))) ++cnt; + if ( cnt >= 3 ) return 1; + } + } + return 0; + } + signed char WallKickValue(int gem_num, int x, int y, int spin, signed char wallkick_spin) const { + if ( ! isWallKickSpin( x, y, getGem(gem_num, spin) ) ) { + return wallkick_spin = 0; + } + if ( isEnableAllSpin() ) { + if ( wallkick_spin == 2) { + wallkick_spin = 1; + Gem g = getGem(gem_num, spin); + for ( int dy = 0; dy < 4; ++dy ) { //KOS mini test + if ( g.bitmap[dy] == 0 ) continue; + if ( ((g.bitmap[dy] << x) | row[y+dy]) == m_w_mask ) continue; + wallkick_spin = 2; + break; + } + } + } else { + if ( wallkick_spin == 2 ) { + if ( ! isCollide( x, y, getGem(gem_num, spin^2) ) ) { + wallkick_spin = 1; // not t-mini + } + } + } + return wallkick_spin; + } + int clearLines( signed char _wallkick_spin ) { + int clearnum = 0; + int h2 = m_h; + for (int h = m_h; h >= -gem_add_y; --h) { + if ( row[h] != m_w_mask) { + row[h2--] = row[h]; + } else { + ++ clearnum; + } + } + for (int h = h2; h >= -gem_add_y; --h) { + row[h] = 0; + } + if ( clearnum > 0 ) { + ++combo; + if ( clearnum == 4 ) { + ++b2b; + } else if ( _wallkick_spin > 0 ) { + ++b2b; + } else { + b2b = 0; + } + } else { + combo = 0; + } + hashval = hash(*this); + return clearnum; + } + int getPCAttack() const { + return m_pc_att; + } + int getAttack( int clearfull, signed char wallkick ) { + int attack = 0; + if ( clearfull > 1 ) { + if ( clearfull < 4 ) { + attack = clearfull - 1; + } else { + attack = clearfull; + if ( b2b > 1 ) attack += 1; + } + } + if ( clearfull > 0 ) { + if ( wallkick ) { + if ( isEnableAllSpin() ) { + attack += clearfull + 1; + if ( wallkick == 2 ) { // mini + attack -= 1; // mini minus + } + } else { + if ( b2b > 1 ) attack += 1; + if ( clearfull == 1 ) { + if ( wallkick == 2 ) { // mini + attack += 1; + } else { + attack += 2; + } + } else { + attack += clearfull + 1; + } + if ( clearfull == 3 ) { + if ( b2b > 1 ) attack += 1; + } + } + } + attack += getComboAttack( combo ); + { + int i = gem_add_y + m_h; + for ( ; i >= 0; --i ) { + if ( m_row[i] ) break; + } + if ( i < 0 ) { + attack += m_pc_att; // pc + } + } + } + return attack; + } + void addRow( int rowdata ) { + for ( int h = -gem_add_y + 1; h <= m_h; ++h ) { + row[h-1] = row[h]; + } + row[m_h] = rowdata; + } + void minusRow( int lines ) { + //row += lines; + //m_h -= lines; + } + }; +} diff --git a/tetris_ai/genmove.cpp b/tetris_ai/genmove.cpp new file mode 100644 index 0000000..3d3169c --- /dev/null +++ b/tetris_ai/genmove.cpp @@ -0,0 +1,715 @@ +#include "tetris_ai.h" +#include +#define USING_MOV_D 0 +#define GENMOV_W_MASK 15 +#define SWITCH_USING_HEIGHT_OPT + +#define _MACRO_CREATE_MOVINGSIMPLE(arg_action_name,arg_wkspin) \ + MovingSimple nm = m; \ + nm.x = nx; \ + nm.y = ny; \ + nm.spin = ns; \ + nm.lastmove = Moving::##arg_action_name##; \ + nm.wallkick_spin = arg_wkspin + +#define _MACRO_CREATE_MOVING(arg_action_name,arg_wkspin) \ + Moving nm = m; \ + nm.x = nx; \ + nm.y = ny; \ + nm.spin = ns; \ + nm.movs.push_back(Moving::##arg_action_name##); \ + nm.wallkick_spin = arg_wkspin +#define _MACRO_HASH_POS(arg_hash_table,arg_prefix) \ + arg_hash_table[##arg_prefix##y][##arg_prefix##s][##arg_prefix##x & GENMOV_W_MASK] + +namespace AI { + bool g_spin180 = false; + std::vector g_combo_attack; + bool g_allSpin = false; + bool g_softdrop = true; + //bool g_softdrop = false; + + void setSpin180( bool enable ) { + g_spin180 = enable; + } + bool spin180Enable() { + return g_spin180; + } + + void setComboList( std::vector combolist ) { + g_combo_attack = combolist; + } + int getComboAttack( int combo ) { + if ( g_combo_attack.empty() ) return 0; + if ( combo >= (int)g_combo_attack.size() ) return g_combo_attack.back(); + return g_combo_attack[combo]; + } + void setAllSpin(bool allSpin) { + g_allSpin = allSpin; + } + bool isEnableAllSpin() { + return g_allSpin; + } + void setSoftdrop( bool softdrop ) { + g_softdrop = softdrop; + } + bool softdropEnable() { + return g_softdrop; + } + //enum { + // MOV_SCORE_DROP = 1, + // MOV_SCORE_LR = 10, + // MOV_SCORE_LR2 = 19, + // MOV_SCORE_LLRR = 100, + // MOV_SCORE_D = 1000, + // MOV_SCORE_DD = 3000, + // MOV_SCORE_SPIN = 20, + //}; + enum { + MOV_SCORE_DROP = 1, + MOV_SCORE_LR = 80, + MOV_SCORE_LR2 = 200, + MOV_SCORE_LLRR = 100, + MOV_SCORE_D = 1000, + MOV_SCORE_DD = 3000, + MOV_SCORE_SPIN = 150, + }; + + void GenMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold) { + assert( cur.num != 0 ); + movs.clear(); + if ( field.isCollide(x, y, getGem(cur.num, cur.spin) ) ) { + return ; + } + //if ( field.isCollide(x, y + 1, getGem(cur.num, cur.spin) ) ) { + // return ; + //} + char _hash[64][4][GENMOV_W_MASK+1] = {0}; + char _hash_drop[64][4][GENMOV_W_MASK+1] = {0}; + char (*hash)[4][GENMOV_W_MASK+1] = &_hash[gem_add_y]; + char (*hash_drop)[4][GENMOV_W_MASK+1] = &_hash_drop[gem_add_y]; + MovList q(1024); + +#ifdef SWITCH_USING_HEIGHT_OPT + // height of every column + int field_w = field.width(), field_h = field.height(); + int min_y[32] = {0}; + { + int beg_y = -5; + while ( field.row[beg_y] == 0 ) ++beg_y; + for ( int x = 0; x < field_w; ++x ) { + for ( int y = beg_y, ey = field_h + 1; y <= ey; ++y ) { + if ( field.row[y] & ( 1 << x ) ) { + min_y[x] = y; + break; + } + } + } + } +#endif + if ( 1 || field.row[y+3] & field.m_w_mask ) // ·Ç¿ÕÆøÐеĻ° + { + MovingSimple m; + m.x = x; + m.y = y; + m.spin = cur.spin; + m.wallkick_spin = 0; + if ( hold ) { + m.lastmove = MovingSimple::MOV_HOLD; + m.hold = true; + } else { + m.lastmove = MovingSimple::MOV_NULL; + m.hold = false; + } + q.push(m); + hash[m.y][m.spin][m.x & GENMOV_W_MASK] = 1; + } + else + { + for ( int spin = 0; spin < 4; ++spin ) + { + int ns = (cur.spin + spin) % cur.mod; + int dx = 0; + for ( ; ; ++dx ) + { + MovingSimple m; + int nx = x + dx, ny = y; + if ( field.isCollide(nx, ny, getGem(cur.num, ns) ) ) + break; + m.x = nx; + m.y = ny; + m.spin = ns; + m.wallkick_spin = 0; + int dist_min = 0x7fffffff; + for ( int x = 0; x < 4; ++x ) { + if ( getGemColH(cur.num, ns, x) ) { // 0 = empty column + int dist_cur_col = min_y[nx + x] - (ny + getGemColH(cur.num, ns, x)); + if ( dist_cur_col < dist_min ) { + dist_min = dist_cur_col; + } + } + } + if ( dist_min < 0 ) { // underground + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + if ( !USING_MOV_D && (_MACRO_HASH_POS(hash,n) & 1) == 0) { + _MACRO_HASH_POS(hash,n) |= 1; + } + ++ny; //wallkick_spin = 0; + } + } else { // under the sun + ny = ny + dist_min; + //if ( dist_min > 0 ) wallkick_spin = 0; + //for ( int y = m.y + 1; y < ny; ++y ) { + // if ( !USING_MOV_D && (hash[y][ns][nx & GENMOV_W_MASK] & 1) == 0) { + // hash[y][ns][nx & GENMOV_W_MASK] |= 1; + // } + //} + } + m.y = ny; + m.lastmove = MovingSimple::MOV_NULL; + if ( hold ) { + m.hold = true; + } else { + m.hold = false; + } + q.push(m); + _MACRO_HASH_POS(hash,n) = 1; + //hash[ny][ns][nx & GENMOV_W_MASK] |= 1; + } + if ( dx > 0 ) + for ( dx = -1; ; --dx ) + { + MovingSimple m; + int nx = x + dx, ny = y; + if ( field.isCollide(nx, ny, getGem(cur.num, ns) ) ) + break; + m.x = nx; + m.y = y; + m.spin = ns; + m.wallkick_spin = 0; + int dist_min = 0x7fffffff; + for ( int x = 0; x < 4; ++x ) { + if ( getGemColH(cur.num, ns, x) ) { // 0 = empty column + int dist_cur_col = min_y[nx + x] - (ny + getGemColH(cur.num, ns, x)); + if ( dist_cur_col < dist_min ) { + dist_min = dist_cur_col; + } + } + } + if ( dist_min < 0 ) { // underground + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + if ( !USING_MOV_D && (_MACRO_HASH_POS(hash,n) & 1) == 0) { + _MACRO_HASH_POS(hash,n) = 1; + } + ++ny; //wallkick_spin = 0; + } + } else { // under the sun + ny = ny + dist_min; + //if ( dist_min > 0 ) wallkick_spin = 0; + //for ( int y = m.y + 1; y < ny; ++y ) { + // if ( !USING_MOV_D && (hash[y][ns][nx & GENMOV_W_MASK] & 1) == 0) { + // hash[y][ns][nx & GENMOV_W_MASK] |= 1; + // } + //} + } + m.y = ny; + m.lastmove = MovingSimple::MOV_NULL; + if ( hold ) { + m.hold = true; + } else { + m.hold = false; + } + q.push(m); + _MACRO_HASH_POS(hash,n) = 1; + //hash[ny][ns][nx & GENMOV_W_MASK] |= 1; + } + } + } + while ( ! q.empty() ) { + MovingSimple m; + q.pop(m); + //if ( m.y < -1 ) continue; + if ( m.lastmove == MovingSimple::MOV_DROP ) { + if ( getGemMaxH(cur.num, m.spin) + m.y <= 2 ) //lockout + continue; + movs.push_back(m); + continue; + } + + if ( m.lastmove != MovingSimple::MOV_DD && m.lastmove != MovingSimple::MOV_D ) + { + int nx = m.x, ny = m.y, ns = m.spin; + int wallkick_spin = m.wallkick_spin; +#ifndef SWITCH_USING_HEIGHT_OPT + while ( field.row[ny + cur.geth()] == 0 && ny + cur.geth() <= field.height() ) { // ·Ç¿ÕÆøÐвÅÄÜʹÓõÄÓÅ»¯ + ++ny; wallkick_spin = 0; + } + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + if ( !USING_MOV_D && ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_HASH_POS(hash, n) |= 1; + } + ++ny; wallkick_spin = 0; + } +#endif +#ifdef SWITCH_USING_HEIGHT_OPT + { + int dist_min = 0x7fffffff; + for ( int x = 0; x < 4; ++x ) { + if ( getGemColH(cur.num, ns, x) ) { // 0 = empty column + int dist_cur_col = min_y[nx + x] - (ny + getGemColH(cur.num, ns, x)); + if ( dist_cur_col < dist_min ) { + dist_min = dist_cur_col; + } + } + } + if ( dist_min < 0 ) { // underground + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + if ( !USING_MOV_D && (_MACRO_HASH_POS(hash,n) & 1) == 0) { + _MACRO_HASH_POS(hash,n) |= 1; + } + ++ny; wallkick_spin = 0; + } + } else { // under the sun + ny = ny + dist_min; + if ( dist_min > 0 ) wallkick_spin = 0; + for ( int y = m.y + 1; y < ny; ++y ) { + if ( !USING_MOV_D && (hash[y][ns][nx & GENMOV_W_MASK] & 1) == 0) { + hash[y][ns][nx & GENMOV_W_MASK] |= 1; + } + } + } + } +#endif + { + int v_spin = (isEnableAllSpin() || cur.num == GEMTYPE_T) ? wallkick_spin : 0; + if ( (_MACRO_HASH_POS(hash_drop, n) & ( 1 << v_spin)) == 0 ) + { + + int _nx = nx, _ny = ny, _ns = ns; + + //if ( cur.num == GEMTYPE_I || cur.num == GEMTYPE_Z || cur.num == GEMTYPE_S ) { + // if ( ns == 2 ) { + // _ny = ny + 1; + // _ns = 0; + // } + // if ( ns == 3 ) { + // _nx = nx + 1; + // _ns = 1; + // } + //} + + //if ( (_MACRO_HASH_POS(hash_drop, _n) & ( 1 << v_spin)) == 0 ) + { + _MACRO_CREATE_MOVINGSIMPLE(MOV_DROP, v_spin); + _MACRO_HASH_POS(hash_drop, _n) |= 1 << v_spin; + q.push(nm); + } + } + if ( softdropEnable() ) // DD + { + if ( ny != y ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_DD, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + --nx; + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_L, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + if ( m.lastmove != MovingSimple::MOV_L && m.lastmove != MovingSimple::MOV_R + && m.lastmove != MovingSimple::MOV_LL && m.lastmove != MovingSimple::MOV_RR ) + { + int nx = m.x - 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx - 1, ny, getGem(cur.num, ns) ) ) { + --nx; + } + if ( nx != x && ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LL, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + ++nx; + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_R, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + if ( m.lastmove != MovingSimple::MOV_L && m.lastmove != MovingSimple::MOV_R + && m.lastmove != MovingSimple::MOV_LL && m.lastmove != MovingSimple::MOV_RR ) + { + int nx = m.x + 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx + 1, ny, getGem(cur.num, ns) ) ) { + ++nx; + } + if ( nx != x && ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RR, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } + } + } +#if USING_MOV_D > 0 + if ( m.lastmove != MovingSimple::MOV_DD ) + { + int nx = m.x, ny = m.y, ns = m.spin; + ++ny; + MovingSimple nm = m; + while ( field.row[ny + cur.geth()] == 0 && ny + cur.geth() <= field.height() ) { // ·Ç¿ÕÆøÐвÅÄÜʹÓõÄÓÅ»¯ + ++ny; + nm.lastmove = MovingSimple::MOV_D; + } + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_D, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } +#endif + { + int nx = m.x, ny = m.y, ns = (m.spin + 1) % cur.mod; + if ( ns != m.spin ) { + if ( (isEnableAllSpin() || cur.num == GEMTYPE_T) ) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 1 ) ) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LSPIN, 1); + _MACRO_HASH_POS(hash, n) |= 1 << 1; + q.push(nm); + } + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 0 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 2 ) ) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LSPIN, 2); + _MACRO_HASH_POS(hash, n) |= 1 << 2; + q.push(nm); + } + } + } else { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) + || field.wallkickTest(nx, ny, getGem(cur.num, ns), 0 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_LSPIN, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = (m.spin + 3) % cur.mod; + if ( ns != m.spin ) { + if ( (isEnableAllSpin() || cur.num == GEMTYPE_T) ) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 1 ) ) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RSPIN, 1); + _MACRO_HASH_POS(hash, n) |= 1 << 1; + q.push(nm); + } + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 1 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 2 ) ) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RSPIN, 2); + _MACRO_HASH_POS(hash, n) |= 1 << 2; + q.push(nm); + } + } + } else { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) + || field.wallkickTest(nx, ny, getGem(cur.num, ns), 1 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0 ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_RSPIN, 0); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } + } + } + if ( spin180Enable() && m.lastmove != MovingSimple::MOV_SPIN2 ) // no 180 wallkick only + { + int nx = m.x, ny = m.y, ns = (m.spin + 2) % cur.mod; + if ( ns != m.spin ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVINGSIMPLE(MOV_SPIN2, 1); + _MACRO_HASH_POS(hash, n) |= 1; + q.push(nm); + } + } + } + } + } + } + + void FindPathMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold) { + movs.clear(); + if ( field.isCollide(x, y, getGem(cur.num, cur.spin) ) ) { + return ; + } + char _hash[64][4][GENMOV_W_MASK+1] = {0}; + char _hash_drop[64][4][GENMOV_W_MASK+1] = {0}; + char (*hash)[4][GENMOV_W_MASK+1] = &_hash[gem_add_y]; + char (*hash_drop)[4][GENMOV_W_MASK+1] = &_hash_drop[gem_add_y]; + MovQueue q(1024); + { + Moving m; + m.x = x; + m.y = y; + m.spin = cur.spin; + m.wallkick_spin = 0; + if ( hold ) { + m.movs.push_back(Moving::MOV_HOLD); + } else { + m.movs.push_back(Moving::MOV_NULL); + } + m.score = 0; + q.push(m); + //hash[m.y][m.spin][m.x & GENMOV_W_MASK] = 1; + } + while ( ! q.empty() ) { + Moving m; + q.pop(m); + if ( m.movs.back() == Moving::MOV_DROP) { + movs.push_back(m); + continue; + } + { + if ( (isEnableAllSpin() || cur.num == GEMTYPE_T) ) { + if ( hash[m.y][m.spin][m.x & GENMOV_W_MASK] & ( 1 << m.wallkick_spin ) ) + continue; + hash[m.y][m.spin][m.x & GENMOV_W_MASK] |= 1 << m.wallkick_spin; + } else { + if ( hash[m.y][m.spin][m.x & GENMOV_W_MASK] & 1 ) + continue; + hash[m.y][m.spin][m.x & GENMOV_W_MASK] |= 1; + } + } + + if ( m.movs.back() != Moving::MOV_DD && m.movs.back() != Moving::MOV_D) + { + int nx = m.x, ny = m.y, ns = m.spin; + int wallkick_spin = m.wallkick_spin; + //while ( field.row[ny + cur.geth()] == 0 && ny + cur.geth() <= field.height() ) { // ·Ç¿ÕÆøÐвÅÄÜʹÓõÄÓÅ»¯ + // ++ny; wallkick_spin = 0; + //} + while ( ! field.isCollide(nx, ny + 1, getGem(cur.num, ns) ) ) { + //if ( !USING_MOV_D && ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + // _MACRO_HASH_POS(hash, n) |= 1; + //} + ++ny; wallkick_spin = 0; + } + { + int v_spin = (isEnableAllSpin() || cur.num == GEMTYPE_T) ? wallkick_spin : 0; + if ( (_MACRO_HASH_POS(hash_drop, n) & ( 1 << v_spin )) == 0 ) + { + int _nx = nx, _ny = ny, _ns = ns; + //if ( (_MACRO_HASH_POS(hash_drop, _n) & ( 1 << v_spin)) == 0 ) + { + _MACRO_CREATE_MOVING(MOV_DROP, v_spin); + _MACRO_HASH_POS(hash_drop, _n) |= 1 << v_spin; + q.push(nm); + } + } + if ( softdropEnable() ) { + if ( ny != y ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_CREATE_MOVING(MOV_DD, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + nm.score += MOV_SCORE_DD - nm.movs.size(); + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + --nx; + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_L, 0); + //_MACRO_HASH_POS(hash, n) = 1; + if ( m.movs.back() != Moving::MOV_L ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_LR2; + q.push(nm); + if ( m.movs.back() != Moving::MOV_L && m.movs.back() != Moving::MOV_R + && m.movs.back() != Moving::MOV_LL && m.movs.back() != Moving::MOV_RR ) + { + int nx = m.x - 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx - 1, ny, getGem(cur.num, ns) ) ) { + --nx; + } + if ( nx != x && ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_CREATE_MOVING(MOV_LL, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + nm.score += MOV_SCORE_LLRR; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = m.spin; + ++nx; + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_R, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + if ( m.movs.back() != Moving::MOV_R ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_LR2; + q.push(nm); + if ( m.movs.back() != Moving::MOV_L && m.movs.back() != Moving::MOV_R + && m.movs.back() != Moving::MOV_LL && m.movs.back() != Moving::MOV_RR ) + { + int nx = m.x + 1, ny = m.y, ns = m.spin; + while ( ! field.isCollide(nx + 1, ny, getGem(cur.num, ns) ) ) { + ++nx; + } + if ( nx != x && ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + _MACRO_CREATE_MOVING(MOV_RR, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + nm.score += MOV_SCORE_LLRR; + q.push(nm); + } + } + } + } + } + //if (USING_MOV_D) + if ( m.movs.back() != Moving::MOV_DD ) + { + int nx = m.x, ny = m.y, ns = m.spin; + ++ny; + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_D, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + nm.score += MOV_SCORE_D; + q.push(nm); + } + } + } + { + int nx = m.x, ny = m.y, ns = (m.spin + 1) % cur.mod; + if ( ns != m.spin ) { + if ( (isEnableAllSpin() || cur.num == GEMTYPE_T) ) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 1 ) ) == 0 ) { + _MACRO_CREATE_MOVING(MOV_LSPIN, 1); + //_MACRO_HASH_POS(hash, n) |= 1 << 1; + if ( m.movs.back() != Moving::MOV_LSPIN ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 0 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 2 ) ) == 0 ) { + _MACRO_CREATE_MOVING(MOV_LSPIN, 2); + //_MACRO_HASH_POS(hash, n) |= 1 << 2; + if ( m.movs.back() != Moving::MOV_LSPIN ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } + } else { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) + || field.wallkickTest(nx, ny, getGem(cur.num, ns), 0 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0 ) { + _MACRO_CREATE_MOVING(MOV_LSPIN, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + if ( m.movs.back() != Moving::MOV_LSPIN ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } + } + } + } + { + int nx = m.x, ny = m.y, ns = (m.spin + 3) % cur.mod; + if ( ns != m.spin ) { + if ( (isEnableAllSpin() || cur.num == GEMTYPE_T) ) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 1 ) ) == 0 ) { + _MACRO_CREATE_MOVING(MOV_RSPIN, 1); + //_MACRO_HASH_POS(hash, n) |= 1 << 1; + if ( m.movs.back() != Moving::MOV_RSPIN ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } else if ( field.wallkickTest(nx, ny, getGem(cur.num, ns), 1 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & ( 1 << 2 ) ) == 0 ) { + _MACRO_CREATE_MOVING(MOV_RSPIN, 2); + //_MACRO_HASH_POS(hash, n) |= 1 << 2; + if ( m.movs.back() != Moving::MOV_RSPIN ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } + } else { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) + || field.wallkickTest(nx, ny, getGem(cur.num, ns), 1 ) ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0 ) { + _MACRO_CREATE_MOVING(MOV_RSPIN, 0); + //_MACRO_HASH_POS(hash, n) |= 1; + if ( m.movs.back() != Moving::MOV_RSPIN ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } + } + } + } + if ( spin180Enable() ) //&& m.movs.back() != Moving::MOV_SPIN2 ) // no 180 wallkick only + { + int nx = m.x, ny = m.y, ns = (m.spin + 2) % cur.mod; + if ( ns != m.spin ) { + if ( ( _MACRO_HASH_POS(hash, n) & 1 ) == 0) { + if ( ! field.isCollide(nx, ny, getGem(cur.num, ns) ) ) { + _MACRO_CREATE_MOVING(MOV_SPIN2, 1); + //_MACRO_HASH_POS(hash, n) |= 1; + if ( m.movs.back() != Moving::MOV_SPIN2 ) + nm.score += MOV_SCORE_LR; + else + nm.score += MOV_SCORE_SPIN; + q.push(nm); + } + } + } + } + } + } +} diff --git a/tetris_ai/graphics.h b/tetris_ai/graphics.h new file mode 100644 index 0000000..e0feb86 --- /dev/null +++ b/tetris_ai/graphics.h @@ -0,0 +1,46 @@ +/********************************************************* +* EGE (Easy Graphics Engine) +* FileName graphics.h +* HomePage1 http://misakamm.github.com/xege +* HomePage2 http://misakamm.bitbucket.org/index.htm +* HomePage3 http://tcgraphics.sourceforge.net +* teiba1 http://tieba.baidu.com/f?kw=ege +* teiba2 http://tieba.baidu.com/f?kw=ege%C4%EF +* Blog: http://misakamm.com +* E-Mail: mailto:misakamm[at gmail com] +* +* FileName: graphics.h +* ÔÚ VC ÏÂÄ£Äâ Borland BGI »æͼ¿â£¬ÊµÏÖ¼òµ¥µÄ»æͼ֮Ó࣬À©Õ¹Á˽ϸ´ÔӵĻæͼÄÜÁ¦ +* +* °üº¬²¢Ê¹Óñ¾¿âʱ£¬²»Òª°üº¬conio.hÍ·Îļþ +* ÕâЩͷÎļþ²»Ó¦¹²´æ£¬·ñÔò¿ÉÄÜ»á±àÒë´íÎó£¬ +* »òÕßgetch±»conio.hÄڵĸ²¸Ç£¨ÓÉ°üº¬´ÎÐò¾ö¶¨£©£¬Çë×¢Òâ +* ÈçÐè¹²´æ£¬ÇëʹÓöàÎļþ·Ö¿ª°üº¬µÄģʽʹÓ㬠+* ¼´²»ÄÜÒ»¸öcppͬʱ°üº¬£¬µ«¿ÉÒÔ·Ö¿ª°üº¬ +* ʹÓñ¾¿â£¬±ØÐëÓÃC++±àÒ룬¿ÉÖ§³ÖµÄ±àÒëÆ÷£º +* VC6/VC2008/VC2010/MinGW3.4.5/MinGW4.4.1 +*********************************************************/ + +/**************************************************************************** +** ×¢ÒâÊÂÏ +* ¡ïÈç¹ûÐèÒªÏÔʾ¿ØÖÆ̨´°¿Ú£¬ÇëÔÚ°üº¬±¾ÎļþµÄÇ°Ãæ¼ÓÒ»ÐÐdefine SHOW_CONSOLE +* ¡ïµ÷ÓÃSleepÕâ¸öAPIʱ£¬»òÕßµ÷ÓÃdelay£¬Êµ¼Ê¾ù»áת»¯Îªµ÷ÓÃdelay_ms£¬Èç±ØÐèµ÷ÓÃAPIÇëʹÓÃapi_sleep +* ¡ïdelay_ms(0)ÄÜ×ÔÐÐÅжÏÓÐûÓиüеıØÒª£¬Á¬Ðø¶à´Îµ«²»´óÁ¿µÄµ÷Óò¢²»»á²úÉúÖ¡ÂʵÄÓ°Ïì +* ¡ïµ÷ÓÃdelay_ms, delay_fps, getch, GetMouseMsg ʱ£¬´°¿ÚÄÚÈÝ¿ÉÄÜ»á¸üУ¬ÕâЩº¯ÊýÏ൱ÓÚÄÚÖÃÁËdelay_ms(0)£¬ +* Èç¹ûÄãÖ»ÐèÒª¸üд°¿Ú£¬¶ø²»ÏëµÈ´ý£¬¿ÉÒÔÓÃdelay_ms(0),Sleep(0)»òÕßdelay(0) +* ¡ïÈç¹ûÄãʹÓÃÁËËø¶¨Ä£Ê½»æͼ£¬ÄÇ¿ÉÒÔʹÓÃWindowFlush´úÌædelay(0)£¬µ«²»ÄÜ´úÌæ·Ç0µÄdelay +* ¡ïºÏÀíµØʹÓÃdelayº¯Êý£¬¿ÉÒÔ¼õÉÙÄãµÄ³ÌÐòÕ¼ÓõÄCPU£¬·ñÔòÒ»¸ödelay¶¼Ã»Óе÷ÓõĻ°£¬³ÌÐò½«Õ¼ÂúÒ»¸öCPUµÄʱ¼ä +* ¡ïsetfillstyleº¯ÊýµÄ×Ô¶¨ÒåÌî³äģʽδʵÏÖ£¬µ«Ò»°ãÓ¦ÓÃ×ã¹» +****************************************************************************/ + +#ifndef _GRAPHICS_H_ +#define _GRAPHICS_H_ + +#ifndef __cplusplus +#error You must use C++ compiler, or you need filename with '.cpp' suffix +#endif + +#include "ege.h" +using namespace ege; + +#endif diff --git a/tetris_ai/graphics08.lib b/tetris_ai/graphics08.lib new file mode 100644 index 0000000..2a27e86 Binary files /dev/null and b/tetris_ai/graphics08.lib differ diff --git a/tetris_ai/graphics12.lib b/tetris_ai/graphics12.lib new file mode 100644 index 0000000..a630e94 Binary files /dev/null and b/tetris_ai/graphics12.lib differ diff --git a/tetris_ai/main.cpp b/tetris_ai/main.cpp new file mode 100644 index 0000000..b9d126f --- /dev/null +++ b/tetris_ai/main.cpp @@ -0,0 +1,1499 @@ +#define _CRT_SECURE_NO_WARNINGS +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + +#include "tetrisgame.h" +#include "gamemode.h" + +#include "graphics.h" + +#include "profile.h" +#include + + +PIMAGE colorCell( int w, int h, color_t normal, color_t lt, color_t rb ) { + PIMAGE img; + img = newimage(w, h); + setbkcolor(normal, img); + setcolor(lt, img); + line(0, 0, w, 0, img); + line(0, h, 0, 0, img); + line(1, 1, w, 1, img); + line(1, 1, 1, h, img); + setcolor(rb, img); + line(w - 1, 0, w - 1, h - 1, img); + line(0, h - 1, w, h - 1, img); + line(w - 2, h - 2, w - 2, 0, img); + line(w - 2, h - 2, 0, h - 2, img); + return img; +} + +/* +float gemColor[8] = { // QQTetris + 0.0f, + 0.0f, + 300.0f, + 90.0f, + 180.0f, + 60.0f, + 210.0f, + 140.0f, +}; +/*/ +float gemColor[8] = { // TOP + 0.0f, + 180.0f, + 300.0f, + 30.0f, + 210.0f, + 0.0f, + 140.0f, + 60.0f, +}; +//*/ + +float gemColorS = 0.7f; + +void tetris_draw_next(const TetrisGame& tetris, PIMAGE* gem) { + setcolor(EGERGB(0x40, 0xa0, 0x40)); + for ( int i = 1; i <= 1; ++i ) { + int bx = int(tetris.m_base.x + 5); + int by = int(tetris.m_base.y + tetris.m_size.y * 1); + rectangle(bx - i, + by - i, + int(bx + tetris.m_size.x * 4) + i, + int(by + tetris.m_size.y * 3) + i + ); + } + outtextxy(int(tetris.m_base.x + 5), int(tetris.m_base.y), "HOLD"); + if ( tetris.m_pool.m_hold > 0 ) { + AI::Gem hold = AI::getGem( tetris.m_pool.m_hold, 0 ); + for ( int y = 0; y < 4; ++y) { + for ( int x = 0; x < 4; ++x) { + if ( hold.bitmap[y] & ( 1 << x ) ) { + int bx = int(tetris.m_base.x + 5 + tetris.m_size.x * x); + int by = int(tetris.m_base.y + tetris.m_size.y * (y + 1)); + if ( tetris.m_pool.m_hold > 1 && tetris.m_pool.m_hold < 7 ) { + bx += int(tetris.m_size.x / 2); + } + if ( tetris.m_pool.m_hold > 1 ) { + by += int(tetris.m_size.y / 2); + } + putimage(bx, by, gem[tetris.m_pool.m_hold]); + } + } + } + } + setcolor(EGERGB(0x40, 0x40, 0xff)); + xyprintf(int(tetris.m_base.x + tetris.m_size.x * (tetris.poolw() + 5) + tetris.m_size.x / 2), int(tetris.m_base.y), + "NEXT %3d", (tetris.n_pieces - (tetris.m_pool.m_hold == AI::GEMTYPE_NULL)) - 1); + for ( int i = 1; i <= 1; ++i ) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (tetris.poolw() + 5) + tetris.m_size.x / 2); + int by = int(tetris.m_base.y + tetris.m_size.y * 1 ); + rectangle(bx - i, + by - i, + int(bx + tetris.m_size.x * 4) + i, + int(by + tetris.m_size.y * 3 * DISPLAY_NEXT_NUM) + i + ); + } + for ( int n = 0; n < DISPLAY_NEXT_NUM; ++n) { + for ( int y = 0; y < 4; ++y) { + for ( int x = 0; x < 4; ++x) { + if (tetris.getNextGemCell(n, x, y)) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (tetris.poolw() + 5) + tetris.m_size.x * x + tetris.m_size.x / 2); + int by = int(tetris.m_base.y + tetris.m_size.y * (y + 1 + n * 3)); + if ( tetris.m_next[n].num > 1 && tetris.m_next[n].num < 7 ) { + bx += int(tetris.m_size.x / 2); + } + if ( tetris.m_next[n].num > 1 ) { + by += int(tetris.m_size.y / 2); + } + putimage(bx, by, gem[tetris.m_next[n].num]); + } + } + } + } +} + +void tetris_draw(const TetrisGame& tetris, bool showAttackLine, bool showGrid) { + static PIMAGE pool_gem[9] = {0}, pool_gem_dark[9] = {0}; + PIMAGE* gem = pool_gem_dark; + if ( pool_gem[1] == NULL ) { + for ( int i = 1; i < 8; ++i ) { + pool_gem[i] = colorCell((int)tetris.m_size.x, (int)tetris.m_size.y, + hsv2rgb(gemColor[i], gemColorS, 0.6f), + hsv2rgb(gemColor[i], gemColorS, 0.9f), + hsv2rgb(gemColor[i], gemColorS, 0.4f)); + } + pool_gem[8] = colorCell((int)tetris.m_size.x, (int)tetris.m_size.y, + EGERGB(0x80,0x80,0x80), EGERGB(0xb0,0xb0,0xb0), EGERGB(0x60,0x60,0x60)); + for ( int i = 1; i < 8; ++i ) { + pool_gem_dark[i] = colorCell((int)tetris.m_size.x, (int)tetris.m_size.y, + hsv2rgb(gemColor[i], gemColorS, 0.3f), + hsv2rgb(gemColor[i], gemColorS, 0.45f), + hsv2rgb(gemColor[i], gemColorS, 0.2f)); + } + pool_gem_dark[8] = colorCell((int)tetris.m_size.x, (int)tetris.m_size.y, + EGERGB(0x40,0x40,0x40), EGERGB(0x50,0x50,0x50), EGERGB(0x30,0x30,0x30)); + } + setbkmode(TRANSPARENT); + tetris_draw_next(tetris, pool_gem); + if ( tetris.alive() ) { + gem = pool_gem; + } + int base_y = int(tetris.m_base.y - tetris.m_size.y); + int min_y[32]; + if ( showGrid ) { + setcolor(EGERGB(0x20, 0x20, 0x20)); + for ( int x = 1; x < tetris.poolw(); ++x) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (x + 5)); + int by = int(base_y + tetris.m_size.y * 3); + line_f( bx, by - 5, bx, by + (int)(tetris.m_size.y * (tetris.poolh() - 2)) ); + line_f( bx-1, by - 5, bx-1, by + (int)(tetris.m_size.y * (tetris.poolh() - 2)) ); + } + for ( int y = 3; y <= tetris.poolh(); ++y) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (5)); + int by = int(base_y + tetris.m_size.y * y); + line_f( bx, by, bx + (int)(tetris.m_size.x * tetris.poolw()), by); + line_f( bx, by-1, bx + (int)(tetris.m_size.x * tetris.poolw()), by-1); + } + } + for ( int x = 0; x < tetris.poolw(); ++x) { + min_y[x] = tetris.poolh() + 1; + for ( int y = 0; y <= tetris.poolh(); ++y) { + if (tetris.getPoolCell(x, y)) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (x + 5)); + int by = int(base_y + tetris.m_size.y * y); + if ( y == 2 ) putimage(bx, by + (int)tetris.m_size.y - 5, (int)tetris.m_size.x, 5, gem[tetris.getPoolCell(x, y)], 0, (int)tetris.m_size.y - 5); + else if ( y > 2 ) putimage(bx, by, gem[tetris.getPoolCell(x, y)]); + min_y[x] = min( min_y[x], y ); + } + } + } + + setcolor(EGERGB(0xa0, 0x0, 0x80)); + for ( int i = 1; i <= 3; ++i ) { + rectangle(int(tetris.m_base.x + tetris.m_size.x * (5)) - i, + int(tetris.m_base.y + tetris.m_size.y * 2) - 5 - i, + int(tetris.m_base.x + tetris.m_size.x * (tetris.poolw() + 5)) + i, + int(tetris.m_base.y + tetris.m_size.y * (tetris.poolh())) + i + ); + } + + if ( tetris.alive() && tetris.m_cur.num ) { + int dy = 0; + if ( ! tetris.m_pool.isCollide( tetris.m_cur_x, tetris.m_cur_y + dy, tetris.m_cur ) ) { + while ( ! tetris.m_pool.isCollide( tetris.m_cur_x, tetris.m_cur_y + dy + 1, tetris.m_cur ) ) { + ++dy; + } + } + if ( dy > 0 ) { + //setlinewidth(3); + setfillcolor( hsv2rgb(gemColor[tetris.m_cur.num], gemColorS, 0.6f) ); + //setcolor(EGERGB(0xcf, 0x4f, 0xcf)); + for ( int y = 0; y < 4; ++y) { + for ( int x = 0; x < 4; ++x) { + if ( y + dy + tetris.cury() <= 2 ) continue; + if (tetris.getCurGemCell(x, y)) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (x + tetris.curx() + 5)); + int by = int(base_y + tetris.m_size.y * (y + dy + tetris.cury())); + int bw = tetris.m_size.x; + int bh = tetris.m_size.y; + //putimage(bx, by, pool_gem_dark[tetris.m_cur.num]); + if ( x == 0 || tetris.getCurGemCell(x-1, y) == 0 ) { //L + bar( bx, by, bx + 3, by + bh ); + } + if ( y == 0 || tetris.getCurGemCell(x, y-1) == 0 ) { //T + bar( bx, by, bx + bw, by + 3 ); + } + if ( x == 3 || tetris.getCurGemCell(x+1, y) == 0 ) { //R + bar( bx + bw - 3, by, bx + bw, by + bh ); + } + if ( y == 3 || tetris.getCurGemCell(x, y+1) == 0 ) { //B + bar( bx, by + bh - 3, bx + bw, by + bh); + } + bar( bx, by, bx + 3, by + 3 ); + bar( bx + bw - 3, by, bx + bw, by + 3 ); + bar( bx + bw - 3, by + bh - 3, bx + bw, by + bh ); + bar( bx, by + bh - 3, bx + 3, by + bh ); + } + } + } + //setlinewidth(1); + } + } + + for ( int y = 0; y < 4; ++y) { + for ( int x = 0; x < 4; ++x) { + if ( tetris.cury() + y <= 1 ) continue; + if ( tetris.getCurGemCell(x, y) ) { + int bx = int(tetris.m_base.x + tetris.m_size.x * (x + tetris.curx() + 5)); + int by = int(base_y + tetris.m_size.y * (y + tetris.cury())); + if ( tetris.cury() + y == 2 ) { + putimage(bx, by + (int)tetris.m_size.y - 5, (int)tetris.m_size.x, 5, pool_gem[tetris.m_cur.num], 0, (int)tetris.m_size.y - 5); + } else { + putimage(bx, by, pool_gem[tetris.m_cur.num]); + } + } + } + } + if ( ! tetris.accept_atts.empty() ) { + int atts = 0; + for ( std::vector::const_iterator it = tetris.accept_atts.begin(); it != tetris.accept_atts.end(); ++it ) { + atts += *it; + } + + setfillcolor(hsv2rgb(0.0f, 1.0f, 1.0f)); + int bx = int(tetris.m_base.x + tetris.m_size.x * (4)) + tetris.m_size.x / 2; + int by = int(base_y + tetris.m_size.y * (tetris.poolh() + 1) ); + bar(bx, + by - (int)tetris.m_size.y * atts, + bx + (int)tetris.m_size.x / 2, + by); + if ( showAttackLine ) { + setcolor (EGERGB(0x80, 0x0, 0x0)); + setlinestyle( DOTTED_LINE ); + int lastbx = -1, lastby = 0; + for ( int x = 0; x < tetris.poolw(); ++x) { + int y = min_y[x]; + int bx = int(tetris.m_base.x + tetris.m_size.x * (x + 5)); + int by = int(base_y + tetris.m_size.y * (y - atts)); + if ( lastbx != -1 && lastby != by ) { + line( lastbx, lastby, bx, by); + } + line( bx, by, bx + (int)tetris.m_size.x, by); + lastbx = bx + (int)tetris.m_size.x; + lastby = by; + } + setlinestyle( SOLID_LINE ); + } + } + { + setcolor (EGERGB(0xa0, 0xa0, 0xa0)); + int combo = tetris.m_pool.combo; + if ( combo > 0 ) combo--; + //xyprintf(int(tetris.m_base.x + tetris.m_size.x * (5)), int(tetris.m_base.y + tetris.m_size.y * ( tetris.poolh() + 0 ) + 1 ), + // "Win %2d Att %2d Combo %d/%d/%d", tetris.n_win, tetris.total_atts, combo, tetris.m_max_combo, tetris.last_max_combo); + double apl = 0, app = 0, pps = 0, apm = 0; + if ( tetris.total_clears > 0 ) { + apl = double(tetris.total_atts)/tetris.total_clears; + } + if ( tetris.n_pieces > 0 ) { + app = double(tetris.total_atts)/tetris.n_pieces; + } + if ( tetris.m_drop_frame > 0 ) { + pps = (tetris.n_pieces - 1) * 60.0 / tetris.m_drop_frame; + } + if ( tetris.m_drop_frame > 0 ) { + apm = tetris.total_atts * 60.0 * 60.0 / tetris.m_drop_frame; + } + rectprintf( + //int(tetris.m_base.x + tetris.m_size.x * (5+tetris.poolw())) + 1, + //int(tetris.m_base.y + tetris.m_size.y * ( tetris.poolh() - 5 ) ), + int(tetris.m_base.x) + 2, + int(tetris.m_base.y + tetris.m_size.y * ( 4 ) + 1 ), + 200, + 400, +#ifdef XP_RELEASE + "Win %4d\nPPS %.2f\nAPM %4.1f\nAtt %4d\nSent %3d\nRecv %3d\nAPL %.2f\nAPP %.3f\nCombo %2d\nClear %2d\nCbA %4d\nB2B %4d\nT0 %5d\nT1 %5d\nT2 %5d\nT3 %5d\n1 %6d\n2 %6d\n3 %6d\n4 %6d\n", + tetris.n_win, pps, apm, tetris.total_atts, tetris.total_sent, tetris.total_accept_atts, apl, app, tetris.m_max_combo, + tetris.m_clear_info.total_pc, tetris.m_clear_info.total_cb_att, tetris.m_clear_info.total_b2b, + tetris.m_clear_info.t[0], tetris.m_clear_info.t[1], tetris.m_clear_info.t[2], tetris.m_clear_info.t[3], + tetris.m_clear_info.normal[1], tetris.m_clear_info.normal[2], tetris.m_clear_info.normal[3],tetris.m_clear_info.normal[4] +#else + "Win %4d\nPPS %.2f\nAPM %4.1f\nAtt %4d\nSent %3d\nRecv %3d\nAPL %.2f\nAPP %.3f\nCombo %2d\nClear %2d\nCbA %4d\nB2B %4d\nT0 %5d\nT1 %5d\nT2 %5d\nT3 %5d\n1 %6d\n2 %6d\n3 %6d\n4 %6d\n", + tetris.n_win, pps, apm, tetris.total_atts, tetris.total_sent, tetris.total_accept_atts, apl, app, tetris.m_max_combo, + tetris.m_clear_info.total_pc, tetris.m_clear_info.total_cb_att, tetris.m_clear_info.total_b2b, + tetris.m_clear_info.t[0], tetris.m_clear_info.t[1], tetris.m_clear_info.t[2], tetris.m_clear_info.t[3], + tetris.m_clear_info.normal[1], tetris.m_clear_info.normal[2], tetris.m_clear_info.normal[3],tetris.m_clear_info.normal[4] +#endif + ); + } + if ( tetris.m_clear_info.attack > 0 ) { + std::string info; + char str[128]; + int att = tetris.m_clear_info.attack; + int b2b = tetris.m_clear_info.b2b > 1; + int combo = tetris.m_clear_info.combo; + int wallkick_spin = tetris.m_clear_info.wallkick_spin; + { + sprintf(str, "+%d ", att); + info += str; + } + att -= b2b; + att -= AI::getComboAttack( combo ); + if ( tetris.m_clear_info.pc ) { + att -= 6; + } + + if ( wallkick_spin ) { + char gemMap[] = " ITLJZSO"; + if ( wallkick_spin == 2 && (AI::isEnableAllSpin() || tetris.m_clear_info.clears == 1) ) { + //if ( wallkick_spin == 2 ) { + sprintf(str, "%c-mini ", gemMap[tetris.m_clear_info.gem_num]); + info += str; + } else { + sprintf(str, "%c-spin%d ", gemMap[tetris.m_clear_info.gem_num], tetris.m_clear_info.clears); + info += str; + } + } else if ( tetris.m_clear_info.gem_num == 1 && tetris.m_clear_info.clears == 4 ) { + sprintf(str, "Quadruple "); + info += str; + } else if ( tetris.m_clear_info.clears == 3 ) { + sprintf(str, "Triple "); + info += str; + } else if ( tetris.m_clear_info.clears == 2 ) { + sprintf(str, "Double "); + info += str; + } + if ( b2b ) { + sprintf(str, "b2b "); + info += str; + } + if ( combo > 1 ) { + sprintf(str, "combo%d ", combo-1); + info += str; + } + if ( tetris.m_clear_info.pc ) { + sprintf(str, "Clear! "); + info += str; + } + setcolor (EGERGB(0x00, 0xA0, 0xff)); + xyprintf(int(tetris.m_base.x + tetris.m_size.x * (5)), int(tetris.m_base.y + tetris.m_size.y * ( tetris.poolh() + 0 ) + 1 ), + info.c_str() ); + tetris.m_att_info = info; + } else { + setcolor (EGERGB(0x40, 0x40, 0x40)); + xyprintf(int(tetris.m_base.x + tetris.m_size.x * (5)), int(tetris.m_base.y + tetris.m_size.y * ( tetris.poolh() + 0 ) + 1 ), + tetris.m_att_info.c_str() ); + } + setcolor (EGERGB(0xa0, 0xa0, 0xa0)); + if ( ! tetris.alive() ) { + xyprintf(0, 0, "Press F2 to restart a new game. Press F12 to config your controls"); + } + { + int w = textwidth(tetris.m_name.c_str()); + if ( tetris.pTetrisAI ) setcolor(EGERGB(0xa0, 0x0, 0xff)); + xyprintf(int(tetris.m_base.x + tetris.m_size.x * ( 5 + tetris.poolw() / 2 )) - w / 2, int(tetris.m_base.y + tetris.m_size.y * 0 ), + "%s", tetris.m_name.c_str() ); + } +} + +void setkeyScene( int player_keys[] ) { + cleardevice(); + while ( kbmsg() ) getkey(); + const char* name[] = { + "move left", + "move right", + "soft drop", + "counterclockwise", + "clockwise", + "hold", + "hard drop", + "180rotate", + }; + int t_player_keys[16]; + for ( int i = 0; i < 8; ++i ) { + for ( t_player_keys[i] = 0; is_run() && t_player_keys[i] == 0; delay_fps(60) ) { + if ( kbmsg() ) { + key_msg k = getkey(); + if ( k.key == key_esc ) { + return ; + } + if ( k.msg == key_msg_down ) { + t_player_keys[i] = k.key; + } + } + cleardevice(); + xyprintf(0, 0, "press a key for %s (ESC to cancel & return)", name[i]); + } + } + for ( int i = 0; i < 8; ++i ) { + player_keys[i] = t_player_keys[i]; + } +} + +void loadKeySetting( int player_keys[] ) { + FILE* fp = fopen("misamino.cfg", "r"); + if ( fp ) { + for ( int i = 0; i < 8; ++i) { + fscanf(fp, "%d", &player_keys[i]); + } + fclose(fp); + } +} + +void saveKeySetting( int player_keys[] ) { + FILE* fp = fopen("misamino.cfg", "w"); + if ( fp ) { + for ( int i = 0; i < 8; ++i) { + fprintf(fp, " %d", player_keys[i]); + } + fprintf(fp, "\n"); + fclose(fp); + } +} + +void onGameStart(TetrisGame& tetris, AI::Random& rnd, int index) { + if ( GAMEMODE_4W ) { + static int hole_index; + for ( int j = 0; j < 39; ++j) { + int v = ~(15 << 3) & tetris.m_pool.m_w_mask; + tetris.addRow(v); + } + if ( index == 0 ) { + hole_index = rnd.randint(4); + } + tetris.addRow((((1 << tetris.m_pool.m_w) - 1) & ~(1<<(hole_index + AI::gem_beg_x))) & tetris.m_pool.m_w_mask); + } +#if !defined( XP_RELEASE ) //|| P1_AI + if ( index == 0 ) tetris.addRow(15); + else tetris.addRow(15 << 6); +#endif + if (0) + { + char n[] = "ITSOJLITZJISOTZ"; + std::map gemMap; + gemMap[' '] = AI::GEMTYPE_NULL; + gemMap['I'] = AI::GEMTYPE_I; + gemMap['T'] = AI::GEMTYPE_T; + gemMap['L'] = AI::GEMTYPE_L; + gemMap['J'] = AI::GEMTYPE_J; + gemMap['Z'] = AI::GEMTYPE_Z; + gemMap['S'] = AI::GEMTYPE_S; + gemMap['O'] = AI::GEMTYPE_O; + for ( int k = 0; n[k]; ++k ) { + tetris.m_next[k] = AI::getGem(gemMap[n[k]], 0); + } + tetris.m_pool.m_hold = gemMap['L']; + } + if (0) + { + int m[22][10] = { + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {1, 1, 1, 1, 1, 0, 0, 1, 1, 0}, + {1, 1, 1, 1, 1, 0, 0, 0, 1, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + }; + for ( int j = 0; j < 22; ++j) { + int v = 0; + for ( int k = 0; k < 10; ++k ) { + if ( m[j][k] ) v |= 1 << k; + } + tetris.addRow(v); + } + tetris.m_next[0] = AI::getGem(AI::GEMTYPE_L, 0); + tetris.m_next[1] = AI::getGem(AI::GEMTYPE_Z, 0); + tetris.m_next[2] = AI::getGem(AI::GEMTYPE_T, 0); + tetris.m_next[3] = AI::getGem(AI::GEMTYPE_S, 0); + tetris.m_next[4] = AI::getGem(AI::GEMTYPE_I, 0); + } +} +struct tetris_ai { + int style; + int level; + int PieceMul; + std::string plugin; + tetris_ai() { + style = 2; + level = 4; + PieceMul = 0; + plugin = "dllai.dll"; + } +}; + +struct tetris_rule { + int turnbase; + int garbage; + int spin180; + int GarbageCancel; + int GarbageBuffer; + int GarbageBlocking; + int combo_table_style; + int samesequence; + int turn; + tetris_rule() { + turnbase = 1; + garbage = 0; + spin180 = 0; + GarbageCancel = 1; + GarbageBuffer = 1; + GarbageBlocking = 1; + combo_table_style = 0; + samesequence = 1; + turn = 1; + } +}; +struct tetris_player { + int tojsoftdrop; + int das; + int softdropdelay; + int softdropdas; + int sound_p1; + int sound_p2; + int sound_bgm; + tetris_player() { + tojsoftdrop = 1; + das = 8; + softdropdelay = 10; + softdropdas = 10; + sound_p1 = 1; + sound_p2 = 0; + sound_bgm = 1; + } +}; + +void loadAI(CProfile& config, tetris_ai ai[]) { + for ( int i = 0; i < 2; ++i ) { + if ( i == 0 ) config.SetSection( "AI_P1" ); + else config.SetSection( "AI_P2" ); + if ( config.IsInteger( "style" ) ) { + ai[i].style = config.ReadInteger( "style" ); + if ( i != 0 && ai[i].style == 0 ) ai[i].style = 2; + } + if ( config.IsInteger( "level" ) ) { + ai[i].level = config.ReadInteger( "level" ); + if ( ai[i].level < 0 ) ai[i].level = 0; + if ( ai[i].level > 10 ) ai[i].level = 10; + } + if ( config.IsInteger( "PieceMul" ) ) { + ai[i].PieceMul = config.ReadInteger( "PieceMul" ); + if ( ai[i].PieceMul <= 0 ) ai[i].PieceMul = 1; + } + config.ReadString( "dllplugin", ai[i].plugin ); + } +} +void loadRule(CProfile& config, tetris_rule& rule) { + config.SetSection( "Rule" ); + if ( config.IsInteger( "turnbase" ) ) { + rule.turnbase = config.ReadInteger( "turnbase" ); + } + if ( config.IsInteger( "KOS_turnbase" ) ) { + int kos_turnbase = config.ReadInteger( "KOS_turnbase" ); + if ( kos_turnbase ) rule.turn = 7; + } + if ( config.IsInteger( "spin180" ) ) { + rule.spin180 = config.ReadInteger( "spin180" ); + AI::setSpin180( rule.spin180 ); + } + if ( config.IsInteger( "GarbageStyle" ) ) { + rule.garbage = config.ReadInteger( "GarbageStyle" ); + } + if ( config.IsInteger( "GarbageCancel" ) ) { + rule.GarbageCancel = config.ReadInteger( "GarbageCancel" ); + } + if ( config.IsInteger( "GarbageBuffer" ) ) { + rule.GarbageBuffer = config.ReadInteger( "GarbageBuffer" ); + } + if ( config.IsInteger( "GarbageBlocking" ) ) { + rule.GarbageBlocking = config.ReadInteger( "GarbageBlocking" ); + } + if ( config.IsInteger( "samesequence" ) ) { + rule.samesequence = config.ReadInteger( "samesequence" ); + } + if ( config.IsInteger( "combo_table_style" ) ) { + rule.combo_table_style = config.ReadInteger( "combo_table_style" ); + } +} +void loadPlayerSetting(CProfile& config, tetris_player& player) { + config.SetSection( "Player" ); + if ( config.IsInteger( "tojsoftdrop" ) ) { + player.tojsoftdrop = config.ReadInteger( "tojsoftdrop" ); + } + if ( config.IsInteger( "das" ) ) { + player.das = config.ReadInteger( "das" ); + if ( player.das < 0 ) player.das = 0; + } + if ( config.IsInteger( "softdropdas" ) ) { + player.softdropdas = config.ReadInteger( "softdropdas" ); + if ( player.softdropdas < 0 ) player.softdropdas = 0; + } + if ( config.IsInteger( "softdropdelay" ) ) { + player.softdropdelay = config.ReadInteger( "softdropdelay" ); + if ( player.softdropdelay < 0 ) player.softdropdelay = 0; + } + config.SetSection( "Sound" ); + if ( config.IsInteger( "p1sfx" ) ) { + player.sound_p1 = config.ReadInteger( "p1sfx" ); + } + if ( config.IsInteger( "p2sfx" ) ) { + player.sound_p2 = config.ReadInteger( "p2sfx" ); + } + if ( config.IsInteger( "bgm" ) ) { + player.sound_bgm = config.ReadInteger( "bgm" ); + } +} + +std::string getpath( std::string path ) { + for ( int i = path.size() - 1; path[i] != '/' && path[i] != '\\'; --i) path[i] = 0; + return path.c_str(); +} + +void mainscene() { + tetris_rule rule; + tetris_player player; + tetris_ai ai[2]; + int ai_first_delay = 30; + int ai_move_delay = 20; + int ai_4w = 1; + + + bool showAttackLine = true; + bool showGrid = true; + int game_pause = 0; + + CProfile config; +#if !defined( XP_RELEASE ) || P1_AI || !PUBLIC_VERSION + config.SetFile( "tetris_ai.ini" ); +#else + config.SetFile( "misamino.ini" ); +#endif + if ( 0 ) + { + config.SetSection( "AI" ); + config.WriteInteger( "level", 6 ); + config.WriteInteger( "depth", 6 ); + config.SetSection( "Rule" ); + config.WriteInteger( "garbage", 0 ); + } + else + { + config.SetSection( "AI" ); + if ( config.IsInteger( "delay" ) ) { + ai_first_delay = config.ReadInteger( "delay" ); + if ( ai_first_delay < 0 ) ai_first_delay = 0; + } + if ( config.IsInteger( "move" ) ) { + ai_move_delay = config.ReadInteger( "move" ); + if ( ai_move_delay < 0 ) ai_move_delay = 0; + } + if ( config.IsInteger( "4w" ) ) { + ai_4w = config.ReadInteger( "4w" ); + } + + loadAI(config, ai); + loadRule(config, rule); + loadPlayerSetting(config, player); + + if ( rule.turn != 1 ) { + ai[0].PieceMul = ai[1].PieceMul = 1; + } + } + + setfont(18, 0, "Courier New", 0, 0, 100, 0, 0, 0 ); + int players_num = 2; + std::vector tetris(players_num); + AI::Random rnd( (unsigned) time( 0 ) ); + int player_keys[8] = { + key_left, + key_right, + key_down, + 'Z', + 'C', + 'V', + key_space, + 'X' + }; + loadKeySetting( player_keys ); +#ifndef XP_RELEASE + EvE eve; + //eve.loadResult(); + if ( ! eve.loadResult() ) + { + //std::deque _p; + //_p.push_back(tetris[0].m_ai_param); + //eve.init( _p ); + eve.ai.push_back(tetris[0].m_ai_param); + } +#endif +#ifdef XP_RELEASE + int ai_eve = 0; + int ai_search_height_deep = DISPLAY_NEXT_NUM > 6 ? 6 : DISPLAY_NEXT_NUM; + int player_stratagy_mode = !AI_SHOW; + int mainloop_times = 1; + int normal_delay = 1; +#else + int ai_eve = 1; + int ai_search_height_deep = AI_TRAINING_DEEP; + int player_stratagy_mode = 1; + int mainloop_times = ( AI_TRAINING_DEEP == 0 ? 1000 : (AI_TRAINING_SLOW ? 2 : 50) ); + int normal_delay = 1; +#endif +#if AI_SHOW == 0 + int player_accept_attack = 1; + int player_begin_attack = 0; //17; +#else + int player_accept_attack = 0; + int player_begin_attack = 0; //17; +#endif + //int ai_search_normal_deep = 0; + int ai_mov_time = ( ai_eve | (1&&ai[0].style) ? + (AI_TRAINING_SLOW ? 12 : 0) + : ( + (AI_SHOW ) ? ( GAMEMODE_4W ? 2: 16 ) : ( PLAYER_WAIT ? 2 : 10 ) ) + ); + ai_mov_time /= 2; // fps=60 + int ai_mov_time_base = 13; // ʵ¼ÊÖµÓÉai_mov_time¾ö¶¨£¬³õʼֵ²»ÓÃ¹Ü + + int player_key_state[8] = {0}; + int player_last_key = 0; + + std::string game_info; + int game_info_time = 0; + tetris[0].m_name = "Human"; + GameSound::ins().loadSFX( ); + if ( player.sound_bgm ) GameSound::ins().loadBGM_wait( rnd ); + if ( player.sound_p1 ) tetris[0].soundon( true ); + if ( player.sound_p2 ) tetris[1].soundon( true ); + + { + typedef int (*AIDllVersion_t)(); + AIDllVersion_t pAIDllVersion = NULL; + char path[MAX_PATH]; + ::GetCurrentDirectoryA(MAX_PATH, path); + for ( int i = 0; i < players_num; ++i ) { + if ( ai[i].style == -1 ) + { + ::SetCurrentDirectoryA( (getpath(std::string(path) + "/" + ai[i].plugin)).c_str() ); + HMODULE hModule = ::LoadLibraryA(ai[i].plugin.c_str()); + if ( hModule ) { + pAIDllVersion = (AIDllVersion_t)GetProcAddress(hModule, "AIDllVersion"); + if ( pAIDllVersion && pAIDllVersion() == AI_DLL_VERSION ) { + tetris[i].pAIName = (AI::AIName_t)GetProcAddress(hModule, "AIName"); + tetris[i].pTetrisAI = (AI::TetrisAI_t)GetProcAddress(hModule, "TetrisAI"); + } + } + } + } + ::SetCurrentDirectoryA(path); + } + + if ( 1 ) + { + AI::AI_Param param = { +// 47, 26, 70, 4, 46, 16, 26, 21, 7, 31, 14, 17, 69, 11, 53, 300 + +// 33, 25, 57, 19, 37, 11, 33, 10, 9, 38, 12, 13, 63, 11, 51, 300 +// 37, 24, 50, 29, 53, 15, 26, 12, 13, 36, 11, 7, 69, 12, 53, 300 +// 36, 25, 50, 20, 55, 15, 28, 12, 15, 36, 12, 10, 70, 12, 53, 300 + +// 40, 15, 50, 20, 56, 16, 17, 12, 7, 55, 99, 23, 78, 16, 67, 300 +// 37, 16, 51, 18, 50, 16, 32, 11, 5, 32, 99, 21, 68, 8, 41, 0 //lv1 s lv2 b +// 26, 18, 46, 30, 53, 22, 29, 17, 9, 33, 3, 11, 84, 6, 50, -16 //lv1 b lv2 a +// 28, 21, 57, 28, 48, 17, 18, 12, 8, 29, 17, 22, 78, 10, 65, -10 //lv2 c + +// 21, 20, 66, 40, 27, 22, 48, 26, 8, 71, 13, 24, 92, 7, 69, 300 // a +// 19, 24, 99, 35, 24, 20, 52, 30, 9, 77, 13, 32, 91, 9, 69, 83 // b +// 19, 22, 87, 37, 16, 13, 42, 19, 6, 73, 10, 33, 93, 5, 73, 83 // b+ +// 21, 24, 54, 33, 23, 24, 36, 16, 8, 70, 12, 25, 73, 8, 67, 92 // c +// 23, 20, 66, 36, 24, 21, 46, 27, 14, 77, 15, 32, 93, 5, 67, 85 // s +// 20, 21, 71, 35, 16, 22, 44, 20, 6, 79, 9, 28, 74, 4, 67, 82 // s + +// 49, 18, 76, 33, 39, 27, 32, 25, 22, 99, 41, 29, 96, 14, 60, 300 //s +// 31, 17, 69, 32, 21, 27, 49, 24, 18, 99, 78, 28, 99, 8, 62, 91 //c +// 52, 19, 69, 34, 35, 30, 34, 20, 17, 89, 37, 31, 83, 9, 55, 97 //c +// 53, 16, 73, 33, 26, 28, 30, 22, 22, 79, 18, 28, 93, 5, 63, 95 //a +// 60, 16, 72, 35, 29, 30, 41, 20, 20, 79, 55, 22, 97, 4, 60, 95 // b +// 40, 13, 80, 37, 16, 24, 38, 19, 21, 67, 99, 24, 96, 6, 42, 83 // a + +// 49, 18, 76, 40, 39, 27, 32, 50, 22, 99, 0, 29, 96, 14, 60, 300 //s ogn + +// 45, 28, 84, 21, 35, 27, 56, 30, 9, 64, 13, 18, 97, 11, 29, 300 // cmp +// 43, 16, 80, 20, 38, 26, 53, 30, 3, 70, -17, 19, 96, 18, 30, 300 // b +// 40, 18, 97, 25, 39, 18, 59, 30, 4, 64, -9, 17, 97, 17, 31, 300 // b+ +// 39, 28, 94, 24, 42, 23, 56, 31, 5, 78, -8, 14, 96, 13, 33, 300 //b +// 38, 21, 94, 28, 48, 25, 54, 34, 8, 88, -21, 19, 80, 31, 32, 300 //a +// 43, 27, 87, 34, 50, 32, 68, 26, -10, 83, -2, 14, 89, 6, 27, 300 //a +// 40, 20, 98, 13, 35, 22, 63, 29, 5, 68, -11, 15, 98, 14, 32, 300 //b + +// 48, 27, 88, 25, 34, 23, 52, 26, 3, 63, -14, 19, 34, 5, 33, 300 // a+ +// 47, 27, 92, 31, 38, 28, 52, 29, 5, 61, -6, 25, 92, 9, 33, 300 // b +// 50, 26, 84, 29, 46, 25, 35, 29, 0, 68, -14, 17, 99, 3, 40, 300 // b- +// 37, 30, 95, 34, 32, 26, 44, 33, 11, 56, -11, 22, 37, 12, 35, 300 // a +// 44, 32, 96, 28, 42, 24, 49, 25, -6, 58, 17, 20, 51, 10, 32, 300 // s +// 48, 27, 97, 22, 41, 29, 53, 27, 3, 60, -10, 19, 42, 6, 31, 300 // a+ + +//new param + +// 41, 13, 99, 28, 42, 32, 47, 24, 6, 61, 19, 30, 41, 12, 27, 300 // b +// 41, 25, 91, 28, 41, 26, 54, 31, 7, 43, 16, 35, 8, 12, 24, 300 // b +// 35, 5, 98, 26, 43, 32, 52, 18, 16, 57, 24, 22, 38, 10, 28, 300 // b +// 41, 13, 98, 27, 45, 24, 51, 28, 13, 65, 27, 21, 39, 13, 39, 300 // s +// 39, 15, 99, 26, 41, 28, 50, 26, 8, 68, 23, 22, 36, 14, 28, 300 // a +// 36, 20, 99, 27, 41, 32, 28, 24, 11, 56, 26, 24, 43, 14, 27, 300 //s + +//* +// 41, 13, 99, 25, 46, 27, 49, 27, 9, 73, 23, 26, 25, 9, 42, 300 // a +// 44, 13, 98, 31, 51, 30, 53, 27, 16, 56, 29, 27, 34, 16, 24, 300 // s +// 39, 13, 98, 29, 30, 34, 54, 28, 18, 58, 20, 28, 35, 11, 33, 300 // s- +// 36, 11, 90, 29, 31, 34, 52, 22, 20, 66, 26, 26, 38, 11, 30, 300 // a +// 34, 29, 97, 35, 24, 23, 55, 27, 14, 75, 19, 41, 32, 0, 46, 300 // a +// 39, 12, 98, 30, 32, 27, 55, 25, 15, 68, 22, 25, 30, 9, 33, 300 // s- +//*/ +// 46, 10, 99, 86, 80, 29, 30, 24, -22, 71, 50, 36, 47, 37, 44, 83 // a +// 74, 39, 88, 99, 57, 33, 30, 23, -14, 73, 60, 43, 58, 39, 38, 96 // a +// 46, 38, 96, 98, 63, 37, 28, 26, -17, 69, 64, 43, 45, 42, 42, 99 // s +// 68, 34, 99, 99, 75, 38, 33, 20, -16, 80, 56, 31, 61, 36, 43, 88 +// 42, 48, 99, 95, 63, 27, -2, 29, -22, 60, 54, 31, 57, 38, 37, 99 // b +// 70, 30, 96, 99, 73, 41, 41, 18, -20, 82, 51, 35, 55, 50, 41, 89 // a +// 36, 30, 71, 67, 72, 48, 22, 16, 34, 60, 0, 34, 46, 35, 16, 33 //test +// 31, 43, 78, 80, 63, 46, 48, 14, 0, 99, 59, 24, 29, 30, 33, 7 //test2 +// 77, 42, 92, 98, 98, 28, 1, 19, -1, 75, 52, 50, 99, 27, 49, 65 //test3 +// 70, 16, 99, 50, 95, 33, 21, 29, 38, 98, 10, 54, 91, 26, 42, 65 + 13, 9, 17, 10, 29, 25, 39, 2, 12, 19, 7, 24, 21, 16, 14, 19, 200 + + }; + tetris[0].m_ai_param = param; + //ai_level_p1 = 4; ai_level_p2 = 4; + } + for ( int i = 0; i < players_num; ++i) { + if ( ai[i].level <= 0 ) { + AI::AI_Param param[2] = { +// 82, 69, 99, 61, 81, 36, 35, 22, 11, 71, 21, 6, 51, 90, 37, -999 +// 21, 20, 66, 40, 27, 22, 48, 26, 8, 71, 13, 24, 92, 7, 69, 999 +// 49, 18, 76, 33, 39, 27, 32, 25, 22, 99, 41, 29, 96, 14, 60, 999 + { + +// 43, 47, 84, 62, 60, 47, 53, 21, 2, 98, 85, 13, 21, 37, 38, 0 +// 47, 66, 86, 66, -79, 42, 38, 23, -3, 95, 74, 13, 27, 36, 37, 0 +// 45, 61, 99, 49, 63, 40, 42, 31, -27, 88, 80, 28, 28, 41, 33, 0 +// 58, 61, 90, 82, 19, 27, 44, 17, -4, 75, 47, 20, 38, 32, 41, 0 + 47, 62, 94, 90, 11, 35, 48, 19, -21, 78, 64, 20, 42, 42, 39, 300 +// 48, 65, 84, 59, -75, 39, 43, 23, -17, 92, 64, 20, 29, 37, 36, 0 + + }, + { + +// 43, 47, 84, 62, 60, 47, 53, 21, 2, 98, 85, 13, 21, 37, 38, 0 +// 47, 66, 86, 66, -79, 42, 38, 23, -3, 95, 74, 13, 27, 36, 37, 0 +// 45, 61, 99, 49, 63, 40, 42, 31, -27, 88, 80, 28, 28, 41, 33, 0 // a +// 58, 61, 90, 82, 19, 27, 44, 17, -4, 75, 47, 20, 38, 32, 41, 0 // b +// 47, 62, 94, 90, 11, 35, 48, 19, -21, 78, 64, 20, 42, 42, 39, 0 // s +// 48, 65, 84, 59, -75, 39, 43, 23, -17, 92, 64, 20, 29, 37, 36, 0 // s + 71, 12, 78, 52, 96, 37, 14, 24, 40, 99, 44, 49, 93, 25, 44, 380 + } +}; + tetris[i].m_ai_param = param[i]; + } + } + std::string ai_name[2] = {"T-spin AI", "T-spin AI"}; + for ( int i = 0; i < players_num; ++i) { + if ( ai[i].style == 1 ) { + ai_name[i] = "T-spin+ AI"; + } else if ( ai[i].style == 2 ) { + AI::setAIsettings(i, "hash", 0); + // + } else if ( ai[i].style == 3 ) { + tetris[i].m_ai_param.tspin = tetris[i].m_ai_param.tspin3 = -300; + tetris[i].m_ai_param.clear_useless_factor *= 0.8; + ai_name[i] = "Ren AI"; + } else if ( ai[i].style == 4 ) { + tetris[i].hold = false; + tetris[i].m_ai_param.clear_useless_factor *= 0.7; + ai_name[i] = "non-Hold"; + // no 4w + tetris[i].m_ai_param.strategy_4w = 0; + AI::setAIsettings(i, "4w", 0); + } else if ( ai[i].style != -1 ) { //if ( ai[i].style == 5 ) { + AI::AI_Param param[2] = { + {49, 918, 176, 33,-300, -0, 0, 25, 22, 99, 41,-300, 0, 14, 290, 0}, // defence AI + {21, 920, 66, 40,-300, -2, 0, 26, 8, 71, 13,-300, 0, 7, 269, 0}, + }; + AI::setAIsettings(i, "combo", 0); + tetris[i].m_ai_param = param[i]; + ai_name[i] = "Downstack"; + } + } + for ( int i = 0; i < players_num; ++i) { + if ( ai[i].style || i > 0 ) + { + char name[200]; + sprintf( name, "%s LV%d", ai_name[i].c_str(), ai[i].level); + tetris[i].m_name = name; + } + if ( tetris[i].pAIName ) { + tetris[i].m_name = tetris[i].pAIName(ai[i].level); + } + if ( ! ai_4w || ai_eve || ai[i].level < 6 ) { + tetris[i].m_ai_param.strategy_4w = 0; + } + if ( tetris[i].m_ai_param.strategy_4w > 0 ) { + AI::setAIsettings(i, "4w", 1); + } + } + if ( rule.combo_table_style == 0 ) + { + int a[] = {0,0,0,1,1,2}; + AI::setComboList( std::vector(a, a + sizeof(a)/sizeof(*a)) ); + for ( int i = 0; i < players_num; ++i) { + tetris[i].m_ai_param.strategy_4w = 0; + AI::setAIsettings(i, "4w", 0); + } + } + else if ( rule.combo_table_style == 1 ) + { + int a[] = {0,0,0,1,1,2,2,3,3,4,4,4,5}; + AI::setComboList( std::vector(a, a + sizeof(a)/sizeof(*a)) ); + } + else if ( rule.combo_table_style == 2 ) + { + int a[] = {0,0,0,1,1,1,2,2,3,3,4,4,4,5}; + AI::setComboList( std::vector(a, a + sizeof(a)/sizeof(*a)) ); + } + else + { + int a[] = {0,0,0,1,1,2}; + AI::setComboList( std::vector(a, a + sizeof(a)/sizeof(*a)) ); + } + if ( rule.GarbageBlocking == 0 || rule.GarbageBuffer == 0 || rule.GarbageCancel == 0 ) { + for ( int i = 0; i < players_num; ++i) { + tetris[i].m_ai_param.strategy_4w = 0; + AI::setAIsettings(i, "4w", 0); + } + } +#ifdef XP_RELEASE + tetris[0].m_state = AI::Tetris::STATE_OVER; + tetris[1].m_state = AI::Tetris::STATE_OVER; +#endif + if ( player.sound_p1 && player.sound_p2 ) { + tetris[0].m_lr = 1; + tetris[1].m_lr = 2; + } + GameSound::ins().setVolume( 0.5f ); + + { + int seed = (unsigned)time(0), pass = rnd.randint(1024); + for ( int i = 0; i < players_num; ++i ) { + tetris[i].m_base = AI::point(i * 400, 30); + //tetris[i].reset( seed ^ ((!rule.samesequence) * i * 255), pass ); + //if ( ai_eve ) + //tetris[i].reset( (unsigned)time(0) + ::GetTickCount() * i ); + /* + int m[20][12] = { + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, + {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, + {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1}, + {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + }; + for ( int j = 0; j < 18; ++j) { + int v = 0; + for ( int k = 0; k < 12; ++k ) { + if ( m[j][k] ) v |= 1 << k; + } + tetris[i].addrow(v); + } + tetris[i].m_next[0] = AI::getGem(2, 0); + */ + /* + int m[22][10] = { + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, + {1, 1, 1, 1, 1, 0, 0, 1, 1, 0}, + {1, 1, 1, 1, 1, 0, 0, 0, 1, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + }; + for ( int j = 0; j < 22; ++j) { + int v = 0; + for ( int k = 0; k < 10; ++k ) { + if ( m[j][k] ) v |= 1 << k; + } + tetris[i].addRow(v); + } + tetris[i].m_next[0] = AI::getGem(AI::GEMTYPE_L, 0); + tetris[i].m_next[1] = AI::getGem(AI::GEMTYPE_Z, 0); + tetris[i].m_next[2] = AI::getGem(AI::GEMTYPE_T, 0); + tetris[i].m_next[3] = AI::getGem(AI::GEMTYPE_S, 0); + tetris[i].m_next[4] = AI::getGem(AI::GEMTYPE_I, 0); + // */ + //onGameStart( tetris[i], rnd, i ); + //tetris[i].acceptAttack(player_begin_attack); + } + } + double ai_time = 0; + int lastGameState = -1; + for ( ; is_run() ; normal_delay ? delay_fps(60) : delay_ms(0) ) { + for ( int jf = 0; jf < mainloop_times; ++jf) { +#ifndef XP_RELEASE + if ( AI_TRAINING_SLOW == 0 ) { + for ( int i = 0; i < players_num; ++i ) { + while ( tetris[i].ai_movs_flag != -1 ) { + ::Sleep(1); + } + } + } + if ( ai_eve ) { + eve.game(tetris[0], tetris[1], rnd); + } +#endif + if ( tetris[0].alive() && tetris[1].alive() ) { + lastGameState = 0; + } else { + if ( lastGameState == 0 ) { + if ( tetris[1].alive() ) { + tetris[0].ko(); + tetris[1].n_win++; + } else { + tetris[1].ko(); + tetris[0].n_win++; + } + //GameSound::ins().stopBGM(); + if ( player.sound_bgm ) GameSound::ins().loadBGM_wait( rnd ); + } + lastGameState = -1; + } + if ( ! ai_eve ) { + while ( kbmsg() ) { + key_msg k = getkey(); + if ( k.msg == key_msg_down && k.key == key_pause ) { + game_pause = ! game_pause; + } + if ( game_pause ) { + continue; + } + if ( player_stratagy_mode && rule.turnbase + && (PLAYER_WAIT || rule.turn > 1) + && k.msg == key_msg_down + && (tetris[0].n_pieces - 1) / rule.turn > (tetris[1].n_pieces - 1) / rule.turn ) + { + bool match = false; + for ( int i = 0; i < 8; ++i ) { + if ( k.key == player_keys[i] ) match = true; + } + player_key_state[0] = 0; + player_key_state[1] = 0; + player_key_state[2] = 0; + if ( match ) continue; + } + if ( k.msg == key_msg_up ) { + for ( int i = 0; i < 8; ++i ) { + if ( player_key_state[i] && (k.key == player_keys[i] ) ) { + player_key_state[i] = 0; + } + } + } + if ( k.msg != key_msg_down ) continue; + //if ( k.key != player_last_key ) { + // player_key_state[0] = 0; + // player_key_state[1] = 0; + // player_key_state[2] = 0; + // player_last_key = k.key; + //} + if ( k.key == player_keys[0] && player_key_state[0] == 0 ) { + tetris[0].tryXMove(-1); + player_key_state[0] = 1; + player_key_state[1] = 0; + //player_key_state[2] = 0; + } + if ( k.key == player_keys[1] && player_key_state[1] == 0 ) { + tetris[0].tryXMove( 1); + player_key_state[0] = 0; + player_key_state[1] = 1; + //player_key_state[2] = 0; + } + if ( k.key == player_keys[2] && player_key_state[2] == 0 ) { + tetris[0].tryYMove( 1); + //while ( tetris[0].tryYMove( 1) ); + //player_key_state[0] = 0; + //player_key_state[1] = 0; + player_key_state[2] = 1; + } + if ( k.key == player_keys[3] && player_key_state[3] == 0 ) { + tetris[0].trySpin( 1); + player_key_state[3] = 1; + } + if ( k.key == player_keys[4] && player_key_state[4] == 0 ) { + tetris[0].trySpin( 3); + player_key_state[4] = 1; + } + if ( k.key == player_keys[5] && player_key_state[5] == 0 ) { + tetris[0].tryHold(); + player_key_state[5] = 1; + } + if ( k.key == player_keys[6] && player_key_state[6] == 0 ) { + tetris[0].drop(); + //player_key_state[0] = !!player_key_state[0]; + //player_key_state[1] = !!player_key_state[1]; + //player_key_state[2] = !!player_key_state[2]; + player_key_state[6] = 1; + } + if ( k.key == player_keys[7] && player_key_state[7] == 0 && AI::spin180Enable() ) { + tetris[0].trySpin180(); + player_key_state[7] = 1; + } + if ( k.key == key_f2 ) { + if ( !tetris[0].alive() || !tetris[1].alive() || tetris[0].n_pieces <= 20 ) { + int seed = (unsigned)time(0), pass = rnd.randint(1024); + for ( int i = 0; i < players_num; ++i ) { + tetris[i].reset( seed ^ ((!rule.samesequence) * i * 255), pass ); + //tetris[i].reset( (unsigned)time(0) + ::GetTickCount() * i ); + onGameStart( tetris[i], rnd, i ); + tetris[i].acceptAttack(player_begin_attack); + } + if ( player.sound_bgm ) GameSound::ins().loadBGM( rnd ); + } + } + if ( k.key == key_f12 ) { + setkeyScene( player_keys ); + for ( int i = 0; i < 7; ++i) + player_key_state[i] = 0; + } + if ( k.key == key_f3 ) { + showAttackLine = !showAttackLine; + } + if ( k.key == key_f4 ) { + showGrid = !showGrid; + } + if ( k.key == key_f5 || k.key == key_f6 ) { + char str[64]; + if ( k.key == key_f5 ) GameSound::ins().setVolumeAdd(-0.05f) ; + if ( k.key == key_f6 ) GameSound::ins().setVolumeAdd(+0.05f); + sprintf_s( str, 64, "%d %%", int(GameSound::ins().getVolume() * 100 + 0.5) ); + game_info = "set volume = "; + game_info += str; + game_info_time = 240; + } + if ( PUBLIC_VERSION == 0 ) { + if ( k.key == key_f9 ) { + tetris[1].accept_atts.push_back(1); + } + } + } + if ( game_pause ) continue; + for (int i = 0; i < 3; ++i) { + if ( player_key_state[i] > 0) { + ++player_key_state[i]; + if ( i == 2 ) player_key_state[i] += 9; + if ( player.tojsoftdrop && i == 2 ) { + while ( player_key_state[i] > (player.softdropdas + 1) * 10 ) { + bool move = tetris[0].tryYMove( 1); + if ( ! move && player.softdropdelay <= 0) break; + player_key_state[i] -= player.softdropdelay; + } + } else if ( player_key_state[i] > player.das + 1 ) { + if ( i == 0 ) { + tetris[0].tryXXMove(-1); + } else if ( i == 1 ) { + tetris[0].tryXXMove( 1) ; + } else if ( i == 2 ) { + tetris[0].tryYYMove( 1) ; + } + } + } + } + } + for ( int i = 0; i < players_num; ++i ) { + if ( tetris[i].game() ) { // ÓÎÏ·Ö´ÐУ¬Èç¹û¶ªÏ·µ»Øtrue + tetris[i].env_change = 1; + tetris[i].n_pieces += 1; + + int att = tetris[i].m_attack; + int clearLines = tetris[i].m_clearLines; +#ifndef XP_RELEASE + if ( tetris[i].total_atts >= 200 && tetris[i].total_atts - tetris[1 - i].total_atts > 20 ) { + tetris[1 - i].m_state = AI::Tetris::STATE_OVER; + } else if ( tetris[i].total_atts >= 400 ) { + if ( (double)tetris[i].total_atts / tetris[i].total_clears > (double)tetris[1 - i].total_atts / tetris[1 - i].total_clears ) { + tetris[1 - i].m_state = AI::Tetris::STATE_OVER; + } + } +#endif + tetris[i].total_clears += clearLines; + tetris[i].m_clearLines = 0; + tetris[i].m_attack = 0; + tetris[i].clearSFX( ); + if ( att > 0 ) { // Á½Ðй¥»÷ + tetris[i].total_atts += att; + if ( rule.GarbageCancel ) { + while ( att > 0 && ! tetris[i].accept_atts.empty() ) { + int m = min( att, tetris[i].accept_atts[0]); + att -= m; + tetris[i].accept_atts[0] -= m; + if ( tetris[i].accept_atts[0] <= 0 ) { + tetris[i].accept_atts.erase( tetris[i].accept_atts.begin() ); + } + } + } + if ( att > 0 ) { + for ( int j = 0; j < players_num; ++j ) { + if ( i == j ) continue; + if ( rule.GarbageBuffer ) { + tetris[j].accept_atts.push_back( att ); + tetris[i].total_sent += att; + if ( rule.turnbase ) tetris[j].env_change = 2; + } else { + if ( player_accept_attack ) + tetris[j].acceptAttack( att ); + if ( rule.turnbase ) tetris[j].env_change = 2; + } + } + } + } + + if ( player_accept_attack && ( rule.GarbageBlocking == 0 || clearLines == 0) ) { + while ( ! tetris[i].accept_atts.empty() ) { + if ( rule.garbage == 0 ) { + tetris[i].acceptAttack( *tetris[i].accept_atts.begin() ); + } else if ( rule.garbage == 1 ) { + for ( int n = *tetris[i].accept_atts.begin(); n > 0; n-= 2 ) { + if ( n >= 2 ) tetris[i].acceptAttack( 2 ); + else tetris[i].acceptAttack( 1 ); + } + } else { //if ( rule.garbage == 2 ) { + for ( int n = *tetris[i].accept_atts.begin(); n > 0; --n ) { + tetris[i].acceptAttack( 1 ); + } + } + tetris[i].accept_atts.erase( tetris[i].accept_atts.begin() ); + } + } + } + if ( tetris[i].env_change && tetris[i].ai_movs_flag == -1) { // AI ¼ÆËã + if ( (ai_eve || ai[i].style) && tetris[i].alive() ) { + //if ( i != 0 && tetris[i].alive() ) { + std::vector next; + for ( int j = 0; j < 32; ++j) + next.push_back(tetris[i].m_next[j]); + double beg = (double)::GetTickCount() / 1000; + int deep = ai_search_height_deep; + int upcomeAtt = 0; + for ( int j = 0; j < tetris[i].accept_atts.size(); ++j ) { + upcomeAtt += tetris[i].accept_atts[j]; + } + int level = ai[i].level; + //if ( tetris[i].m_pool.row[6] ) { + // deep = ai_search_height_deep; + //} + if ( i == 1 && rule.turn == 1 && rule.turnbase && level > 9 ) { // ·À2P±»³¬Ô½Ì«¶à + if ( tetris[0].n_pieces * ai[1].PieceMul - tetris[i].n_pieces * ai[0].PieceMul > 2 ) { + level = 9; + } + } + bool canhold = tetris[i].hold; + + if ( tetris[i].pTetrisAI ) { + AI::RunAIDll(tetris[i].pTetrisAI, tetris[i].ai_movs, tetris[i].ai_movs_flag, tetris[i].m_ai_param, tetris[i].m_pool, tetris[i].m_hold, + tetris[i].m_cur, + tetris[i].m_cur_x, tetris[i].m_cur_y, next, canhold, upcomeAtt, + deep, tetris[i].ai_last_deep, level, i); + } else { + AI::RunAI(tetris[i].ai_movs, tetris[i].ai_movs_flag, tetris[i].m_ai_param, tetris[i].m_pool, tetris[i].m_hold, + tetris[i].m_cur, + tetris[i].m_cur_x, tetris[i].m_cur_y, next, canhold, upcomeAtt, + deep, tetris[i].ai_last_deep, level, i); +#if 1 && !defined(XP_RELEASE) + while ( tetris[i].ai_movs_flag != -1 ) ::Sleep(1); +#endif + } + ai_time = (double)::GetTickCount() / 1000 - beg; + if ( rule.turnbase && ai[0].style == 0 ) { + if ( rule.turn == 1 && tetris[0].n_pieces * ai[1].PieceMul - tetris[i].n_pieces * ai[0].PieceMul > 1 ) { + ai_mov_time_base = ai_mov_time / 4; + } else { + ai_mov_time_base = ai_mov_time; + } + tetris[i].ai_delay = ai_mov_time_base + ai_mov_time_base / 3; + } else { + tetris[i].ai_delay = ai_first_delay; + } + if ( tetris[i].env_change == 2 ) { // ±»¹¥»÷¾Í°´Òѵȴýʱ¼ä¼õÉÙ˼Ë÷µÈ´ý + tetris[i].ai_delay = max(0, tetris[i].ai_delay - tetris[i].m_piecedelay); + } + } + tetris[i].env_change = 0; + } + } + for ( int i = 0; i < players_num; ++i ) { // 100 garbage buffer get lost + if ( ! tetris[i].alive() ) continue; + int total = 0; + for ( int j = 0; j < tetris[i].accept_atts.size(); ++j ) { + total += tetris[i].accept_atts[j]; + } + if ( total >= 100 * 1 ) { // TODO + tetris[i].m_state = AI::Tetris::STATE_OVER; + } + } + for ( int i = 0; i < players_num; ++i ) { + if ( tetris[i].ai_delay > 0 ) --tetris[i].ai_delay; + if ( player_stratagy_mode ) { + if ( i != 0 && tetris[0].n_pieces == 1 ) continue; + if ( rule.turnbase ) { + if ( i == 0 && (tetris[0].n_pieces - 1) * ai[1].PieceMul / rule.turn > (tetris[1].n_pieces - 1) * ai[0].PieceMul / rule.turn ) continue; + if ( i != 0 && (tetris[1].n_pieces - 1) * ai[0].PieceMul / rule.turn >= (tetris[0].n_pieces - 1) * ai[1].PieceMul / rule.turn ) continue; + } else { + if ( i == 0 && !tetris[1].alive() ) continue; + if ( i != 0 && !tetris[0].alive() ) continue; + } + } + //if ( tetris[i].mov_llrr ) { + // if (0) ; + // else if (tetris[i].mov_llrr == AI::Moving::MOV_LL) {if ( ! tetris[i].tryXMove(-1) ) tetris[i].mov_llrr = 0;} + // else if (tetris[i].mov_llrr == AI::Moving::MOV_RR) {if ( ! tetris[i].tryXMove( 1) ) tetris[i].mov_llrr = 0;} + //} else + { + do + { + if ( tetris[i].ai_delay > 0 ) ; + else if ( tetris[i].ai_movs_flag == -1 && ! tetris[i].ai_movs.movs.empty() ){ + if ( rule.turnbase && ai[0].style == 0 ) { + tetris[i].ai_delay = ai_mov_time_base; + } else { + tetris[i].ai_delay = ai_move_delay; + } + int mov = tetris[i].ai_movs.movs[0]; + if ( tetris[i].ai_movs.movs.size() > 1 ) { + int next_mov = tetris[i].ai_movs.movs[1]; + if ( mov != next_mov ) tetris[i].ai_delay = tetris[i].ai_delay * 8 / 12; + } + tetris[i].ai_movs.movs.erase( tetris[i].ai_movs.movs.begin() ); + if (0) ; + else if (mov == AI::Moving::MOV_L) tetris[i].tryXMove(-1); + else if (mov == AI::Moving::MOV_R) tetris[i].tryXMove( 1); + else if (mov == AI::Moving::MOV_D) tetris[i].tryYMove( 1); + else if (mov == AI::Moving::MOV_LSPIN) tetris[i].trySpin(1); + else if (mov == AI::Moving::MOV_RSPIN) tetris[i].trySpin(3); + else if (mov == AI::Moving::MOV_LL) { tetris[i].tryXXMove(-1); } //{ tetris[i].mov_llrr = AI::Moving::MOV_LL; } + else if (mov == AI::Moving::MOV_RR) { tetris[i].tryXXMove( 1); } //{ tetris[i].mov_llrr = AI::Moving::MOV_RR; } + else if (mov == AI::Moving::MOV_DD) tetris[i].tryYYMove( 1) ; + else if (mov == AI::Moving::MOV_DROP) tetris[i].drop(); + else if (mov == AI::Moving::MOV_HOLD) { + tetris[i].tryHold(); + } else if (mov == AI::Moving::MOV_SPIN2) { + if ( AI::spin180Enable() ) { + tetris[i].trySpin180(); + } + } else if (mov == AI::Moving::MOV_REFRESH) { + tetris[i].env_change = 1; + } + } + } while ( tetris[i].ai_movs_flag == -1 && tetris[i].ai_delay == 0 && !tetris[i].ai_movs.movs.empty() ); + } + } + if ( ! ai_eve ) break; + } + cleardevice(); + for (std::vector::iterator it = tetris.begin(); + it != tetris.end(); + ++it) { + tetris_draw(*it, showAttackLine, showGrid); + } + //if(0) + if ( ! PUBLIC_VERSION ) + { + for ( int i = 0; i < players_num; ++i ) { + setcolor(EGERGB(0x0, 0xa0, 0x0)); + //xyprintf(0,0, "%.3f", ai_time * 1000); + xyprintf((int)tetris[i].m_base.x, (int)(tetris[i].m_base.y - tetris[i].m_size.y), "d = %d", tetris[i].ai_last_deep); + } + } + if ( game_info_time > 0 ) { + --game_info_time; + xyprintf( 0, getheight() - textheight("I"), "%s", game_info.c_str()); + } +#ifndef XP_RELEASE + if ( ai_eve ) { + double d = 0; + if ( eve.m_p2 > 0 ) { + d = eve.m_p2_score / (double)eve.m_p2; + d *= d; + d = eve.m_p2_sqr_score / (double)eve.m_p2 - d; + d = eve.m_p2_score / (double)eve.m_p2 - sqrt(d); + } + if ( (eve.m_p1 + eve.m_p2) % 2 == 0) + xyprintf(0, 0, "(%d) %d : %d", eve.ai.size(), eve.m_p2, eve.m_p1); + else + xyprintf(0, 0, "(%d) %d : %d", eve.ai.size(), eve.m_p1, eve.m_p2); + //xyprintf(0, 0, "%.2f %d %d", d * eve.round, eve.m_p2, eve.m_p2_score + tetris[1].total_atts); + } +#endif + { + char buff[16]; + sprintf(buff, "%.2ffps", getfps()); + outtextxy(getwidth() - textwidth(buff), 0, buff); + } + } + saveKeySetting( player_keys ); +} + +void CenterWindow(HWND hWnd) +{ + HWND hParentOrOwner; + RECT rc, rc2; + int x,y; + if ( (hParentOrOwner = GetParent(hWnd)) == NULL ) + { + SystemParametersInfo(SPI_GETWORKAREA,0,&rc,0); + } + else + { + GetClientRect(hParentOrOwner, &rc); + } + GetWindowRect(hWnd, &rc2); + x = ((rc.right-rc.left) - (rc2.right-rc2.left)) / 2 +rc.left; + y = ((rc.bottom-rc.top) - (rc2.bottom-rc2.top)) / 2 +rc.top; + SetWindowPos(hWnd,HWND_TOP,x, y,0, 0,SWP_NOSIZE); +} + +int main () { +#if PUBLIC_VERSION == 0 + setinitmode(INIT_ANIMATION & ~INIT_WITHLOGO); +#else + setinitmode(INIT_ANIMATION); +#endif + initgraph(800, 500); + //CenterWindow( getHWnd() ); +#if PUBLIC_VERSION == 0 + setcaption("Tetris AI Demo"); +#else + setcaption("MisaMino V1.4.5 ---- by Misakamm ( misakamm.com )"); +#endif + mainscene(); + return 0; +} diff --git a/tetris_ai/random.h b/tetris_ai/random.h new file mode 100644 index 0000000..9a93943 --- /dev/null +++ b/tetris_ai/random.h @@ -0,0 +1,183 @@ +#pragma once +//#include + +//************************************************************************ +// This is a slightly modified version of Equamen mersenne twister. +// +// Copyright (C) 2009 Chipset +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +//************************************************************************ + +// Original Coyright (c) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura +// +// Functions for MT19937, with initialization improved 2002/2/10. +// Coded by Takuji Nishimura and Makoto Matsumoto. +// This is a faster version by taking Shawn Cokus's optimization, +// Matthe Bellew's simplification, Isaku Wada's real version. +// C++ version by Lyell Haynes (Equamen) + +namespace AI { + +const int N = 624; +const int M = 397; + +typedef unsigned __int32 uint32; + +class mtrandom { +public: + mtrandom() : left(1) { init(); } + mtrandom(const mtrandom& m) { + for ( int i = 0; i < N; ++i) { + state[i] = m.state[i]; + } + left = m.left; + next = state + (m.next - m.state); + } + + explicit mtrandom(uint32 seed) : left(1) { init(seed); } + + mtrandom(uint32* init_key, int key_length) : left(1) { + int i = 1, j = 0; + int k = N > key_length ? N : key_length; + init(); + for(; k; --k) { + state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1664525UL)) + init_key[j] + j; // non linear + state[i] &= 4294967295UL; // for WORDSIZE > 32 machines + ++i; + ++j; + if(i >= N) { + state[0] = state[N - 1]; + i = 1; + } + if(j >= key_length) + j = 0; + } + + for(k = N - 1; k; --k) { + state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1566083941UL)) - i; // non linear + state[i] &= 4294967295UL; // for WORDSIZE > 32 machines + ++i; + if(i >= N) { + state[0] = state[N - 1]; + i = 1; + } + } + + state[0] = 2147483648UL; // MSB is 1; assuring non-zero initial array + } + + void reset(uint32 rs) { + init(rs); + next_state(); + } + + uint32 rand() { + uint32 y; + if(0 == --left) + next_state(); + y = *next++; + // Tempering + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + return y; + } + + double real() { return (double)rand() / ((double)(unsigned long)(-1L) + 1); } + + // generates a random number on [0,1) with 53-bit resolution + double res53() { + uint32 a = rand() >> 5, b = rand() >> 6; + return (a * 67108864.0 + b) / 9007199254740992.0; + } + +private: + void init(uint32 seed = 19650218UL) { + state[0] = seed & 4294967295UL; + for(int j = 1; j < N; ++j) { + state[j] = (1812433253UL * (state[j - 1] ^ (state[j - 1] >> 30)) + j); + // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. + // In the previous versions, MSBs of the seed affect + // only MSBs of the array state[]. + // 2002/01/09 modified by Makoto Matsumoto + state[j] &= 4294967295UL; // for >32 bit machines + } + } + + void next_state() { + uint32* p = state; + int i; + + for(i = N - M + 1; --i; ++p) + *p = (p[M] ^ twist(p[0], p[1])); + + for(i = M; --i; ++p) + *p = (p[M - N] ^ twist(p[0], p[1])); + *p = p[M - N] ^ twist(p[0], state[0]); + left = N; + next = state; + } + + uint32 mixbits(uint32 u, uint32 v) const { + return (u & 2147483648UL) | (v & 2147483647UL); + } + + uint32 twist(uint32 u, uint32 v) const { + return ((mixbits(u, v) >> 1) ^ (v & 1UL ? 2567483615UL : 0UL)); + } + + uint32 state[N]; + uint32 left; + uint32* next; +}; + +/* +class mtrand_help { + static mtrandom r; +public: + mtrand_help() {} + void operator()(uint32 s) { r.reset(s); } + uint32 operator()() const { return r.rand(); } + double operator()(double) { return r.real(); } +}; +mtrandom mtrand_help:: r; + +extern void mtsrand(uint32 s) { mtrand_help()(s); } +extern uint32 mtirand() { return mtrand_help()(); } +extern double mtdrand() { return mtrand_help()(1.0); } +*/ + +class Random { +public: + Random( unsigned _seed = 0 ) { + seed(_seed); + } + void seed( unsigned seed ) { + r.reset( seed ); + } + uint32 rand() { + return r.rand(); + } + int randint( uint32 maxnum ) { + return int(randfloat() * maxnum); + } + double randfloat () { + return r.real(); + } +private: + mtrandom r; +}; + +} diff --git a/tetris_ai/resource.h b/tetris_ai/resource.h new file mode 100644 index 0000000..773db42 Binary files /dev/null and b/tetris_ai/resource.h differ diff --git a/tetris_ai/resource1.h b/tetris_ai/resource1.h new file mode 100644 index 0000000..0e9756c --- /dev/null +++ b/tetris_ai/resource1.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Tspin08.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/tetris_ai/sound.h b/tetris_ai/sound.h new file mode 100644 index 0000000..832e5df --- /dev/null +++ b/tetris_ai/sound.h @@ -0,0 +1,182 @@ +#pragma once + +#include "bass.h" +#pragma comment(lib, "bass.lib") +#include + +class GameSound { +protected: + GameSound() { + mVolume = 0.5f; + mBgmVolume = 0.3f; + BASS_Init(-1,44100,0,0,NULL); + } + ~GameSound() { + } +public: + class sound { + public: + sound( ) { + for ( int i = 0; i < 16; ++i) + m[i] = NULL; + mOpen = false; + mIndex = 0; + mMax = 1; + } + ~sound() { + for ( int i = 0; i < 16; ++i) { + if ( m[i] ) { + BASS_StreamFree(m[i]); + m[i] = NULL; + } + } + BASS_Free(); + } + int open( float * volume, const char* path, int nMax = 2, int loop = 0 ) { + mVolume = volume; + if ( m[0] ) { + for ( int i = 0; i < 16; ++i) { + if ( m[i] ) { + BASS_StreamFree(m[i]); + m[i] = NULL; + } + } + } + m[0] = BASS_StreamCreateFile(FALSE, path, 0, 0, loop?BASS_SAMPLE_LOOP:0); + int ret = m[0]; + mMax = nMax; + for ( int i = 1; i < mMax; ++i) { + m[i] = BASS_StreamCreateFile(FALSE, path, 0, 0, loop?BASS_SAMPLE_LOOP:0); + } + mOpen = ret != 0; + return ret; + } + bool isOpen() { + return mOpen; + } + int play( int lr = 0) { + mIndex = (mIndex+1) % mMax; + //m[mIndex]->Stop(); + setVolume( *mVolume ); + if ( lr == 1 ) { + BASS_ChannelSetAttribute(m[mIndex], BASS_ATTRIB_PAN, -0.7); + } else if ( lr == 2 ){ + BASS_ChannelSetAttribute(m[mIndex], BASS_ATTRIB_PAN, 0.7); + } + return BASS_ChannelPlay(m[mIndex], TRUE); + } + int stop() { + return BASS_ChannelStop(m[mIndex]); + } + void setVolume( float volume ) { + if ( m[mIndex] ) { + BASS_ChannelSetAttribute( m[mIndex], BASS_ATTRIB_VOL, volume ); + } + } + private: + HSTREAM m[16]; + int mIndex; + int mMax; + bool mOpen; + float * mVolume; + }; + static GameSound& ins() { + static GameSound gamesound; + return gamesound; + } + void loadSFX () { +#if !defined(XP_RELEASE) + return; +#endif + std::string basePath = "sound/sfx/default/"; + mSFX_ko.open(&mVolume, (basePath + "sfx_ko.wav").c_str()); + mSFX_gameover.open(&mVolume, (basePath + "sfx_gameover.wav").c_str()); + mSFX_pc.open(&mVolume, (basePath + "sfx_perfectclear.wav").c_str()); + mSFX_move.open(&mVolume, (basePath + "sfx_move.wav").c_str(), 4); + mSFX_hold.open(&mVolume, (basePath + "sfx_hold.wav").c_str()); + mSFX_rotate.open(&mVolume, (basePath + "sfx_rotate.wav").c_str()); + mSFX_softdrop.open(&mVolume, (basePath + "sfx_softdrop.wav").c_str(), 8); + mSFX_harddrop.open(&mVolume, (basePath + "sfx_harddrop.wav").c_str()); + mSFX_lockdown.open(&mVolume, (basePath + "sfx_lockdown.wav").c_str()); + mSFX_lineattack.open(&mVolume, (basePath + "sfx_lineattack.wav").c_str()); + mSFX_clears[0].open(&mVolume, (basePath + "sfx_single.wav").c_str()); + mSFX_clears[1].open(&mVolume, (basePath + "sfx_double.wav").c_str()); + mSFX_clears[2].open(&mVolume, (basePath + "sfx_triple.wav").c_str()); + mSFX_clears[3].open(&mVolume, (basePath + "sfx_tetris.wav").c_str()); + mSFX_b2b_tetris.open(&mVolume, (basePath + "sfx_b2b_tetris.wav").c_str()); + for ( int i = 0; i < 20; ++i) { + char name[16]; + sprintf( name, "sfx_combo%d.wav", i + 1); + mSFX_combo[i].open(&mVolume, (basePath + name).c_str()); + } + mSFX_tspin[0].open(&mVolume, (basePath + "sfx_tspin_mini.wav").c_str()); + mSFX_tspin[1].open(&mVolume, (basePath + "sfx_tspin_single.wav").c_str()); + mSFX_tspin[2].open(&mVolume, (basePath + "sfx_tspin_double.wav").c_str()); + mSFX_tspin[3].open(&mVolume, (basePath + "sfx_tspin_triple.wav").c_str()); + mSFX_b2b_tspin[0].open(&mVolume, (basePath + "sfx_b2b_tspin_mini.wav").c_str()); + mSFX_b2b_tspin[1].open(&mVolume, (basePath + "sfx_b2b_tspin_single.wav").c_str()); + mSFX_b2b_tspin[2].open(&mVolume, (basePath + "sfx_b2b_tspin_double.wav").c_str()); + mSFX_b2b_tspin[3].open(&mVolume, (basePath + "sfx_b2b_tspin_triple.wav").c_str()); + } + void loadBGM_wait( AI::Random& rnd ) { + std::string basePath = "sound/music/default/"; + int n = rnd.randint(2); + if ( n == 0 ) { + mbgm.open(&mBgmVolume, (basePath + "waiting1.ogg").c_str(), 1, 1); + } else { + mbgm.open(&mBgmVolume, (basePath + "waiting2.ogg").c_str(), 1, 1); + } + mbgm.play(); + } + void loadBGM( AI::Random& rnd ) { + std::string basePath = "sound/music/default/"; + int n = rnd.randint(3); + if ( n == 0 ) { + mbgm.open(&mBgmVolume, (basePath + "bgm_01.ogg").c_str(), 1, 1); + } else if ( n == 1 ) { + mbgm.open(&mBgmVolume, (basePath + "bgm_02.ogg").c_str(), 1, 1); + } else if ( n == 2 ) { + mbgm.open(&mBgmVolume, (basePath + "bgm_03.ogg").c_str(), 1, 1); + } + mbgm.play(); + } + void stopBGM() { + mbgm.stop(); + } + void setVolume( float volume ) { + mVolume = volume; + if ( mVolume < 0.0 ) mVolume = 0; + if ( mVolume > 1.0 ) mVolume = 1.0f; + mBgmVolume = mVolume * 0.6f; + mbgm.setVolume(mBgmVolume); + } + void setVolumeAdd( float add ) { + mVolume += add; + if ( mVolume < 0.0 ) mVolume = 0; + if ( mVolume > 1.0 ) mVolume = 1.0f; + mBgmVolume = mVolume * 0.6f; + mbgm.setVolume(mBgmVolume); + } + float getVolume() const { + return mVolume; + } +public: + sound mSFX_move; + sound mSFX_hold; + sound mSFX_rotate; + sound mSFX_softdrop; + sound mSFX_harddrop; + sound mSFX_lockdown; + sound mSFX_lineattack; + sound mSFX_clears[4]; + sound mSFX_combo[20]; + sound mSFX_tspin[4]; + sound mSFX_b2b_tspin[4]; + sound mSFX_b2b_tetris; + sound mSFX_ko; + sound mSFX_gameover; + sound mSFX_pc; + sound mbgm; + float mVolume; + float mBgmVolume; +}; diff --git a/tetris_ai/tetris.h b/tetris_ai/tetris.h new file mode 100644 index 0000000..8682a0b --- /dev/null +++ b/tetris_ai/tetris.h @@ -0,0 +1,412 @@ +#pragma once +#include "gamepool.h" +#include "random.h" +#include "tetris_ai.h" + +namespace AI { + + struct point { + float x; + float y; + point() : x(0), y(0) {} + point(float _x, float _y) : x(_x), y(_y) {} + point(int _x, int _y) + : x(static_cast(_x)) + , y(static_cast(_y)) + {} + }; + + class Tetris { + public: + enum { + STATE_INIT, + STATE_READY, + STATE_MOVING, + STATE_PASTED, + STATE_OVER, + }; + struct clear_info { + int gem_num; + int clears; + int attack; + int b2b; + int combo; + int pc; + signed char wallkick_spin; + int total_pc; + int total_b2b; + int total_cb_att; + int t[4]; + int normal[5]; + void reset( int _gem_num ) { + memset(this, 0, sizeof(*this)); + gem_num = _gem_num; + } + void newgem( int _gem_num ) { + gem_num = _gem_num; + clears = 0; + attack = 0; + b2b = 0; + combo = 0; + pc = 0; + wallkick_spin = 0; + } + }; + Tetris() : m_pool( 10, 20 ) { + m_state = STATE_INIT; + reset ( 0, 10, 20 ); + } + void genNext() { + int m[] = {1,2,3,4,5,6,7}; + int s[7]; + int v = m_rand.randint(5040); + int mod = 5040 / 7; + for ( int i = 6; i > 0; --i ) { + s[6-i] = m[v / mod]; + for ( int j = v / mod; j < 6; ++j) { + m[j] = m[j+1]; + } + v %= mod; + mod /= i; + } + s[6] = m[0]; + for ( int i = 0; i < 7; ++i ) { + m_next[ m_next_num + i] = AI::getGem( s[i], 0); + } + m_next_num += 7; + } + void reset (unsigned seed, signed char w, signed char h) { + m_pool.reset( w, h ); + m_rand.seed( seed ); + m_next_num = 0; + while ( m_next_num < 100 ) genNext(); + //for ( int i = 0; i < 32; ++i ) { + // m_next[i] = AI::getGem( m_rand.randint(7) + 1, 0); + //} + m_state = STATE_READY; + m_cur = AI::getGem(0, 0); + m_cur_x = AI::gem_beg_x; + m_cur_y = AI::gem_beg_y; + m_curnum = 0; + m_clearLines = 0; + m_attack = 0; + m_max_combo = 0; + m_frames = 0; + m_drop_frame = 0; + m_clear_info.reset( 0 ); + memset( m_color_pool, 0, sizeof( m_color_pool ) ); + } + bool tryXMove(int dx) { + if ( m_state != STATE_MOVING ) return false; + if (m_pool.isCollide(m_cur_x + dx, m_cur_y, m_cur)) + return false; + m_cur_x += dx; + wallkick_spin = 0; + return true; + } + bool tryYMove(int dy) { + if ( m_state != STATE_MOVING ) return false; + if (m_pool.isCollide(m_cur_x, m_cur_y + dy, m_cur)) + return false; + m_cur_y += dy; + wallkick_spin = 0; + return true; + } + bool trySpin(int dSpin) { + if ( m_state != STATE_MOVING ) return false; + AI::Gem gem = AI::getGem(m_cur.num, (m_cur.spin + dSpin + 4) % 4); + if (m_pool.isCollide(m_cur_x, m_cur_y, gem)) { + int spin = 1; + if ( dSpin == 1 ) spin = 0; + if ( m_pool.wallkickTest(m_cur_x, m_cur_y, gem, spin) ) { + m_cur = gem; + wallkick_spin = 2; + return true; + } else { + return false; + } + } + m_cur = gem; + wallkick_spin = 1; + return true; + } + bool trySpin180() { + if ( m_state != STATE_MOVING ) return false; + AI::Gem gem = AI::getGem(m_cur.num, (m_cur.spin + 2) % 4); + if (m_pool.isCollide(m_cur_x, m_cur_y, gem)) { + return false; + } + m_cur = gem; + wallkick_spin = 1; + return true; + } + bool tryHold() { + if ( m_state != STATE_MOVING ) return false; + if ( m_hold ) return false; + m_hold = true; + int hold = m_pool.m_hold; + m_pool.m_hold = m_cur.num; + if ( hold == 0 ) { + m_cur_x = AI::gem_beg_x; + m_cur_y = AI::gem_beg_y; + m_cur = AI::getGem(m_next[0].num, 0); + removeNext(); + } else { + m_cur_x = AI::gem_beg_x; + m_cur_y = AI::gem_beg_y; + m_cur = AI::getGem(hold, 0); + } + if ( m_pool.isCollide(m_cur_x, m_cur_y, m_cur)) { + m_state = STATE_OVER; + return true; + } + return true; + } + void paste() { + for ( int y = 0; y < 4; ++y ) { + for ( int x = 0; x < 4; ++x ) { + if ( m_cur.bitmap[y] & ( 1 << x ) ) { + m_color_pool[m_cur_y + y + 32][m_cur_x + x] = m_cur.num; + } + } + } + } + bool drop () { + if ( m_state != STATE_MOVING ) return false; + m_clear_info.newgem( m_cur.num ); + while ( tryYMove( 1 ) ); + wallkick_spin = m_pool.WallKickValue(m_cur.num, m_cur_x, m_cur_y, m_cur.spin, wallkick_spin); + m_pool.paste( m_cur_x, m_cur_y, m_cur ); + paste(); + m_drop_frame = m_frames; + m_cur = AI::getGem( 0, 0); + m_state = STATE_PASTED; + return true; + } + void color_pool_clearLines() { + int dy = 63; + for ( int y = dy; y >= 0; --y ) { + int x = 0; + for ( ; x < poolw(); ++x ) { + if ( m_color_pool[y][x] == 0 ) break; + } + if ( x < poolw() ) { + if ( dy != y ) { + for (x = 0 ; x < poolw(); ++x ) { + m_color_pool[dy][x] = m_color_pool[y][x]; + } + } + --dy; + } + } + for ( ; dy >= 0; --dy ) { + for (int x = 0 ; x < poolw(); ++x ) { + m_color_pool[dy][x] = 0; + } + } + } + void clearLines () { + if ( m_state != STATE_PASTED ) return; + m_clearLines = m_pool.clearLines( wallkick_spin ); + color_pool_clearLines(); + m_attack = m_pool.getAttack( m_clearLines, wallkick_spin ); + m_max_combo = std::max(m_max_combo, m_pool.combo - 1); + + m_clear_info.clears = m_clearLines; + m_clear_info.attack = m_attack; + m_clear_info.b2b = m_pool.b2b; + m_clear_info.combo = m_pool.combo; + m_clear_info.wallkick_spin = wallkick_spin; + m_clear_info.total_cb_att += getComboAttack( m_pool.combo ); + if ( m_clear_info.b2b > 1 && m_attack > 0 ) + { + ++m_clear_info.total_b2b; + } + { + int i = gem_add_y + m_pool.height(); + for ( ; i >= 0; --i ) { + if ( m_pool.m_row[i] ) break; + } + if ( i < 0 ) { + m_clear_info.pc = 1; + ++m_clear_info.total_pc; + } + } + int special = 0; + //if ( m_clear_info.gem_num == 1 && m_clearLines >= 4 ) + //{ + // special = 1; + // m_clear_info.normal[4] += 1; + //} + if ( m_attack > 0 ) + { + if ( m_clear_info.wallkick_spin ) { + special = 1; + //if ( m_clear_info.gem_num == 2 ) + { + if ( wallkick_spin == 2 && (AI::isEnableAllSpin() || m_clear_info.clears == 1) ) { + //if ( m_clear_info.wallkick_spin == 2 ) { + ++m_clear_info.t[0]; + } else { + ++m_clear_info.t[m_clear_info.clears]; + } + } + } + //if ( m_clear_info.gem_num == 2 ) + //{ + // int att = m_attack; + // att -= m_pool.b2b > 1; + // att -= getComboAttack( m_pool.combo ); + // if ( m_clear_info.pc ) { + // att -= 6; + // } + // if ( att / m_clear_info.clears >= 2 ) { // T1T2T3 + // special = 1; + // ++m_clear_info.t[m_clear_info.clears]; + // } else if ( m_clear_info.clears == 2 ) { // double + // //++m_clear_info.normal[2]; + // } else if ( m_clear_info.clears == 1 && att == 1 ) { // T0 + // special = 1; + // ++m_clear_info.t[0]; + // } + //} + } + if ( m_clearLines > 0 && special == 0 ) + { + ++m_clear_info.normal[m_clearLines]; + } + + m_state = STATE_READY; + } + void addRow( int att ) { + { + for ( int y = 1; y < 64; ++y ) { + for (int x = 0 ; x < poolw(); ++x ) { + m_color_pool[y-1][x] = m_color_pool[y][x]; + } + } + for (int x = 0 ; x < poolw(); ++x ) { + if ( att & ( 1 << x ) ) { + m_color_pool[poolh() + 32][x] = 8; + } else { + m_color_pool[poolh() + 32][x] = 0; + } + } + } + m_pool.addRow( att ); + if ( m_cur_y > 1 ) { + m_cur_y -= 1; + } + if ( m_pool.m_row[0] ) { + m_state = STATE_OVER; + } + } + void setRow( int y, int att ) { + { + for (int x = 0 ; x < poolw(); ++x ) { + if ( att & ( 1 << x ) ) { + m_color_pool[y + 32][x] = 8; + } else { + m_color_pool[y + 32][x] = 0; + } + } + } + m_pool.row[y] = att; + } + void removeNext() { + for (int i = 1; i < m_next_num; ++i) { + m_next[i - 1] = m_next[i]; + } + --m_next_num; + while ( m_next_num < 100 ) genNext(); + //m_next[15] = AI::getGem( m_rand.randint(7) + 1, 0); + } + bool newpiece() { + if ( m_state != STATE_READY ) return false; + m_cur_x = AI::gem_beg_x; + m_cur_y = AI::gem_beg_y; + ++m_curnum; + m_cur = m_next[0]; + m_hold = false; + wallkick_spin = 0; + removeNext(); + //if ( m_pool.row[0] || m_pool.row[1] || m_pool.isCollide(m_cur_x, m_cur_y, m_cur)) { + if ( m_pool.isCollide(m_cur_x, m_cur_y, m_cur) ) { + m_state = STATE_OVER; + return true; + } + m_state = STATE_MOVING; + return true; + } + bool game() { + ++m_frames; + switch (m_state) { + case STATE_MOVING: + { + } + break; + case STATE_PASTED: + { + clearLines(); + } + case STATE_READY: + { + return newpiece(); + } + break; + default: + break; + } + return false; + } + int poolw() const { + return m_pool.width(); + } + int poolh() const { + return m_pool.height(); + } + int curx() const { + return m_cur_x; + } + int cury() const { + return m_cur_y; + } + int getCurGemCell(int x, int y) const { + if ( m_cur.bitmap[y] & ( 1 << x ) ) return 1; + return 0; + } + int getNextGemCell(int next, int x, int y) const { + if ( m_next[next].bitmap[y] & ( 1 << x ) ) return 1; + return 0; + } + int getPoolCell(int x, int y) const { + return m_color_pool[y+32][x]; + //if ( m_pool.row[y + 1] & ( 1 << x) ) return 1; + return 0; + } + bool alive () const { + return m_state != STATE_OVER; + } + public: + int m_state; + public: + Random m_rand; + AI::GameField m_pool; + AI::Gem m_cur; + int m_color_pool[64][32]; + int m_hold; + int m_cur_x, m_cur_y; + int m_curnum; + signed char wallkick_spin; + AI::Gem m_next[128]; + int m_next_num; + point m_base, m_size; + int m_clearLines; + int m_attack; + int m_max_combo; + int m_frames; + int m_drop_frame; + clear_info m_clear_info; + }; + +} diff --git a/tetris_ai/tetris.ico b/tetris_ai/tetris.ico new file mode 100644 index 0000000..ff211c2 Binary files /dev/null and b/tetris_ai/tetris.ico differ diff --git a/tetris_ai/tetris_ai.cpp b/tetris_ai/tetris_ai.cpp new file mode 100644 index 0000000..716fc85 --- /dev/null +++ b/tetris_ai/tetris_ai.cpp @@ -0,0 +1,1898 @@ +#include "tetris_ai.h" +#include +#include +#include +#include + +#include "tetris_setting.h" +#include + +#define GENMOV_W_MASK 15 +#define GEN_MOV_NO_PUSH 0 + +namespace AI { + enum { + MOV_SCORE_DROP = 1, + MOV_SCORE_LR = 10, + MOV_SCORE_LR2 = 30, + MOV_SCORE_LLRR = 20, + MOV_SCORE_D = 50, + MOV_SCORE_DD = 100, + MOV_SCORE_SPIN = 50, + }; + + struct _ai_settings { + bool hash; + bool combo; + bool strategy_4w; + _ai_settings() { + hash = true; + combo = true; + strategy_4w = false; + } + } ai_settings[2]; + + void setAIsettings(int player, const char* key, int val) { + if ( strcmp(key, "hash") == 0 ) { + ai_settings[player].hash = val; + } else if ( strcmp(key, "combo") == 0 ) { + ai_settings[player].combo = val; + } else if ( strcmp(key, "4w") == 0 ) { + ai_settings[player].strategy_4w = val; + } + } + + int Evaluate( int &clearScore, const AI_Param& ai_param, const GameField& last_pool, const GameField& pool, int cur_num, + int curdepth, + int total_clear_att, int total_clears, int clear_att, int clears, signed char wallkick_spin, + int lastCombo, int t_dis, int upcomeAtt + ) { + int score = 0; + // ²â¸ß¶È + //int last_min_y[32] = {0}; + int min_y[32] = {0}; + int emptys[32] = {0}; + int maxy_index = 31, maxy_cnt = 0; + int maxy_flat_cnt = 0; // ×ƽ̨ + int miny_val = 31; + int total_hole = 0; + int beg_y = -5; + const int pool_w = pool.width(), pool_h = pool.height(); + //last_min_y[31] = -1; + min_y[31] = -1; + { + //while ( last_pool.row[beg_y] == 0 ) ++beg_y; + //for ( int x = 0; x < pool_w; ++x) { + // for ( int y = beg_y, ey = pool_h + 1; y <= ey; ++y) { // ÒªÓе×Ðб£»¤£¨pool.h£©£¬·ñÔò»á¹Ò + // if ( last_pool.row[y] & ( 1 << x ) ) { + // last_min_y[x] = y; + // break; + // } + // } + //} + beg_y = -5; + while ( pool.row[beg_y] == 0 ) ++beg_y; + for ( int x = 0; x < pool_w; ++x) { + for ( int y = beg_y, ey = pool_h + 1; y <= ey; ++y) { // ÒªÓе×Ðб£»¤£¨pool.h£©£¬·ñÔò»á¹Ò + if ( pool.row[y] & ( 1 << x ) ) { + min_y[x] = y; + miny_val = std::min(miny_val, y); + if ( y > min_y[maxy_index]) { + maxy_index = x; + maxy_cnt = 0; + } else if ( y == min_y[maxy_index]) { + ++maxy_cnt; + } + break; + } + } + } + int transitions = 0; + for ( int y = beg_y; y <= pool_h; ++y) { + int last = 1; //pool.row[y] & 1; + if ( pool.row[y] > 0 ) { + for ( int x = 0; x < pool_w; ++x) { + if ( pool.row[y] & ( 1 << x ) ) { + if ( last == 0 ) ++transitions; + last = 1; + } else { + if ( last == 1 ) ++transitions; + last = 0; + ++emptys[y]; + } + } + } else { + emptys[y] = pool_w; + } + transitions += !last; + } + score += ai_param.v_transitions * transitions / 10; + } + min_y[pool_w] = min_y[pool_w-2]; + //last_min_y[pool_w] = last_min_y[pool_w-2]; + + if ( pool.m_hold == GEMTYPE_I ) { + score -= ai_param.hold_I; + } + if ( pool.m_hold == GEMTYPE_T ) { + score -= ai_param.hold_T; + } + if ( maxy_cnt > 0 ) { + int ybeg = min_y[maxy_index]; + unsigned rowdata = pool.row[ybeg-1]; + int empty = ~rowdata & pool.m_w_mask; + for ( int b = maxy_index; b < pool_w; ++b ) { + if ( ybeg != min_y[b] ) continue; + int cnt = 1; + for ( int b1 = b + 1; empty & ( 1 << b1); ++b1) ++cnt; + if ( maxy_flat_cnt < cnt ) { + maxy_flat_cnt = cnt; + maxy_index = b; + } + } + } + // ¶´µÄÊýÁ¿ + int x_holes[32] = {0}; // ˮƽ·½Ïò¶´µÄÊýÁ¿ + int y_holes[32] = {0}; // ´¹Ö±·½Ïò¶´µÄÊýÁ¿ + int x_op_holes[32] = {0}; // ˮƽ·½Ïò¶´µÄÊýÁ¿ + //int last_pool_hole_score; + int pool_hole_score; + int pool_total_cell = 0; + //{ // last_pool + // int x_holes[32] = {0}; // ˮƽ·½Ïò¶´µÄÊýÁ¿ + // int x_op_holes[32] = {0}; // ˮƽ·½Ïò¶´µÄÊýÁ¿ + // int first_hole_y[32] = {0}; // ´¹Ö±·½Ïò×î½üµÄ¶´µÄy + // int hole_score = 0; + // const GameField& _pool = last_pool; + // for ( int x = 0; x < pool_w; ++x) { + // int last = 0, next; + // first_hole_y[x] = pool_h + 1; + // for ( int y = last_min_y[x] + 1; y <= pool_h; ++y, last = next) { + // if ( ( _pool.row[y] & ( 1 << x ) ) == 0) { + // next = 1; + // if ( x > 1 ) { + // if (last_min_y[x-1] > y && last_min_y[x-2] > y) { + // if ( last == 0) { + // hole_score += ai_param.open_hole; + // if ( y >= 0 ) ++x_op_holes[y]; + // continue; + // } + // } + // } + // if ( x < pool_w - 2 ) { + // if (last_min_y[x+1] > y && last_min_y[x+2] > y) { + // if ( last == 0) { + // hole_score += ai_param.open_hole; + // if ( y >= 0 ) ++x_op_holes[y]; + // continue; + // } + // } + // } + // if ( y >= 0 ) ++x_holes[y]; + // if ( first_hole_y[x] > pool_h ) { + // first_hole_y[x] = y; + // } + // if ( last ) { + // hole_score += ai_param.hole / 4; + // } else { + // //score += (y - min_y[x]) * ai_param.hole_dis; + // //if ( x_holes[y] > 2 ) { + // // hole_score += ai_param.hole / 4; + // //} else + // if ( x_holes[y] >= 2 ) { + // hole_score += ai_param.hole * x_holes[y]; + // } else { + // hole_score += ai_param.hole * 2; + // } + // } + // } else { + // next = 0; + // } + // } + // } + // //if(1) + // for ( int y = 0; y <= pool_h; ++y) { + // if ( x_holes[y] > 0 ) { + // hole_score += ai_param.hole_dis * (pool_h - y + 1); + // //int min_dis = pool_h; + // //for ( int x = 0; x < pool_w; ++x) { + // // if ( first_hole_y[x] == y ) { + // // min_dis = std::min(min_dis, y - min_y[x]); + // // } + // //} + // //if ( min_dis == 1 ) { + // // bool fill = true; + // // for ( int i = y - min_dis; i < y ; ++i ) { + // // int empty = ~pool.row[i] & pool.m_w_mask; + // // if ( empty & ( empty - 1) ) { + // // fill = false; + // // break; + // // } + // // } + // // if ( fill ) { + // // score -= ai_param.hole_dis; + // // } + // //} + // break; + // } + // } + // //for ( int y = 0; y <= pool_h; ++y) { + // // if ( x_holes[y] ) score += ai_param.has_hole_row; + // //} + // last_pool_hole_score = hole_score; + //} + { // pool + int first_hole_y[32] = {0}; // ´¹Ö±·½Ïò×î½üµÄ¶´µÄy + int x_renholes[32] = {0}; // ´¹Ö±Á¬Ðø¶´µÄÊýÁ¿ + double hole_score = 0; + const GameField& _pool = pool; + for ( int x = 0; x < pool_w; ++x) { + for ( int y = min_y[x]; y <= pool_h; ++y ) { + if ( ( _pool.row[y] & ( 1 << x ) ) == 0 ) { + pool_total_cell++; + } + } + } + for ( int x = 0; x < pool_w; ++x) { + int last = 0, next; + first_hole_y[x] = pool_h + 1; + for ( int y = std::min(min_y[x] + 1, std::max(min_y[x-1] + 6, min_y[x+1] + 6)); y <= pool_h; ++y, last = next) { + if ( ( _pool.row[y] & ( 1 << x ) ) == 0 ) { //_pool.row[y] && + double factor = ( y < 20 ? ( 1 + (20 - y) / 20.0 * 2 ) : 1.0); + y_holes[x]++; + next = 1; + if ( softdropEnable() ) { + if ( x > 1 ) { + if (min_y[x-1] > y && min_y[x-2] > y) { + //if ( last == 0) { + hole_score += ai_param.open_hole * factor; + if ( y >= 0 ) ++x_op_holes[y]; + continue; + //} + } + } + if ( x < pool_w - 2 ) { + if (min_y[x+1] > y && min_y[x+2] > y) { + //if ( last == 0) { + hole_score += ai_param.open_hole * factor; + if ( y >= 0 ) ++x_op_holes[y]; + continue; + //} + } + } + } + if ( y >= 0 ) ++x_holes[y]; + if ( first_hole_y[x] > pool_h ) { + first_hole_y[x] = y; + } + int hs = 0; + if ( last ) { + hs += ai_param.hole / 2; + if ( y >= 0 ) ++x_renholes[y]; + } else { + hs += ai_param.hole * 2; + } + { + //if ( x_holes[y] == 2 ) { + // hs -= ai_param.hole; + //} else if ( x_holes[y] >= 3 ){ + // hs -= ai_param.hole * 2; + //} + ++total_hole; + } + hole_score += hs * factor; + } else { + next = 0; + } + } + } + //for ( int y = 0; y <= pool_h; ++y) { + // if ( x_holes[y] > 1 ) { + // int n = x_holes[y] - x_renholes[y]; + // int hs = 0; + // if ( n == 2 ) + // hs = ai_param.hole + x_renholes[y] * ai_param.hole / 2; + // else if ( n > 2 ) + // hs = (n - 2) * ai_param.hole * 2 + x_renholes[y] * ai_param.hole / 2; + // hole_score -= hs * ( y < 10 ? ( 1 + (10 - y) / 10.0 * 2 ) : 1.0); + // score -= ai_param.v_transitions * x_holes[y] / 10; + // } + //} + for ( int y = 0; y <= pool_h; ++y) { + if ( x_holes[y] > 0 ) { + score += ai_param.hole_dis * (pool_h - y + 1); + break; + } + } + if(1) + if ( ai_param.hole_dis_factor ) { + for ( int y = 0, cnt = 5, index = -1; y <= pool_h; ++y) { + if ( x_holes[y] > 0 ) { + if ( cnt > 0 ) --cnt, ++index; + else break; + for ( int x = 0; x <= pool_w; ++x) { + if ( ( _pool.row[y] & ( 1 << x ) ) == 0) { + int h = y - min_y[x]; + if ( h > 4 - index ) h = 4 + (h - (4 - index)) * cnt / 4; + //if ( h > 4 ) h = 4; + if ( h > 0 ) { + if ( ( _pool.row[y - 1] & ( 1 << x ) ) != 0) { + score += ai_param.hole_dis_factor * h * cnt / 5 / 2; + } + } + } + } + } + } + } + if(1) + if ( ai_param.hole_dis_factor2 ) { + int miny = pool_h; + for ( int y = 0, cnt = 0; y <= pool_h; ++y) { + if ( x_holes[y] > 0 ) { + if ( cnt < 4 ) ++cnt; + else break; + for ( int x = 0; x <= pool_w; ++x) { + if ( ( _pool.row[y] & ( 1 << x ) ) == 0) { + int vy = min_y[x] + cnt * 1; + if ( vy < miny ) miny = vy; + } + } + } + } + for ( int x = 0; x <= pool_w; ++x) { + int vy = min_y[x] + 6; + if ( vy < miny ) miny = vy; + } + double total_emptys = 0; + for ( int y = miny; y <= pool_h; ++y ) { + total_emptys += emptys[y] * ( y < 10 ? (10 + 10 - y ) / 10.0 : 1); + } + score += ai_param.hole_dis_factor2 * total_emptys / 4; + //score += ai_param.hole_dis_factor2 * (pool_h - miny); + //int h = 0; + //h = min_y[maxy_index] - miny - 1; + //if ( h > 0 ) + // score += ai_param.hole_dis_factor2 * h; + //for ( int x = 0; x <= pool_w; ++x) { + // h += min_y[x] - miny; + //} + //if ( h > 0 ) + // score += ai_param.hole_dis_factor2 * h * 2 / pool_w; + } + //for ( int y = 0; y <= pool_h; ++y) { + // if ( x_holes[y] ) score += ai_param.has_hole_row; + //} + pool_hole_score = hole_score; + } + score += pool_hole_score; +#ifdef XP_RELEASE + // È«Ïû + if ( 0 ) //&& pool.getPCAttack() > 8 ) + { + if ( beg_y > pool_h ) + { + //score -= 1000; + clearScore -= (2000 * pool.combo - curdepth * 50) * 1.0; + } + else if ( pool_total_cell % 2 == 0 && beg_y > pool_h - 10 && total_hole < 2 ) + { + int h_hole[32] = {0}; + const GameField& _pool = pool; + int top_y = beg_y, hole_add = 0, height; + if ( pool_total_cell % 4 ) top_y -= 1; + //if ( x_holes[beg_y+1] > 0 ) top_y -= 2; + //if ( top_y > pool_h - 2 ) top_y -= 2; + height = pool_h + 1 - top_y; + if ( height < 3 ) height += 2; + for ( int x = 0; x < pool_w; ++x) { + h_hole[x] = min_y[x] - top_y + y_holes[x]; + } + + int last_cnt = 0, pc = 1, finish = 0, h4 = 0, cnt1 = 0, total_cnt = 0; + for ( int x = 0; x < pool_w; ++x) { + total_cnt += h_hole[x]; + } + for ( int x = 0; x < pool_w; ++x) { + if ( h_hole[x] == 0 ) { + if ( last_cnt % 4 != 0 ) { + //pc = 0; + top_y -= 2; + height += 2; + x = -1; + last_cnt = 0, finish = 0, h4 = 0, cnt1 = 0, total_cnt += pool_w * 2; + { + for ( int x = 0; x < pool_w; ++x) { + h_hole[x] += 2; + } + } + } + else if ( last_cnt <= 0 || last_cnt >= total_cnt - 0 ) finish++; + else ++h4; + } else { + if ( min_y[x] == top_y + 1 ) { + ++cnt1; + } + last_cnt += h_hole[x]; + } + } + //if ( pc && ( height <= 4 || last_cnt < height * 6 ) ) { + if ( pc && ( (total_cnt) / 4 * 0.8 + total_clear_att < pool.getPCAttack() ) ) { //&& last_cnt < 30 ) { + //if ( beg_y > pool_h - 4 ) score -= 500 * 2; + //int s = 1000 * 5 + finish * 400 - h4 * 10 - (height + total_clears) * 800 - cnt1 * 20; + //int s = (50 + finish * 10 + h4 * 1 - (height + total_clears) * 10 - cnt1 * 2) * 2.0; + int s = (30 + finish * 2 + h4 * 1 - (height /*+ total_clears * 2*/) * 3 /*- cnt1 * 2 */) * 1.0; + //int s = 100 * 2 + finish * 3 - h4 * 1 - (height + total_clears) * 10 - cnt1 * 2; + //score -= 1000 * 4 + finish * 100 - last_cnt * 30 - cnt1 * 50; + //score -= 500 * 4 + finish * 100 - last_cnt * 30 - cnt1 * 50 - curdepth * 100; + if ( s > 0 ) + score -= s; + } + } + } +#endif + // ¸ß¶È²î + { + //int n_maxy_index = maxy_index; + //if ( maxy_cnt != 0 ) n_maxy_index = -9; + + int last = min_y[1]; + for ( int x = 0; x <= pool_w; last = min_y[x], ++x) { + int v = min_y[x] - last; + if ( x == maxy_index ) { + x += maxy_flat_cnt; + continue; + } + int absv = abs(v); + if ( x + 1 == maxy_index && v > 0 || x - 2 == maxy_index && v < 0 ) ; // + else score += absv * ai_param.h_factor; + } + } + // ƽµØ + /* + { + int last = -1, len = 0; + for ( int x = 0; x <= pool_w; ++x) { + if ( last == min_y[x] ) { + ++len; + } else { + if ( len > 0 && len < 4) { + score -= ((len - 1) / 3 + 1) * ai_param.flat_factor; + } + len = 0; + last = min_y[x]; + } + } + if ( len > 0 && len < 4) { + score -= ((len - 1) / 3 + 1) * ai_param.flat_factor; + } + } + */ + int center = 10; // °ÚÂ¥¾¯½äÏß + double warning_factor = 1; + int h_variance_score = 0; + // Ëã·½²î + { + int avg = 0; + { + int sum = 0; + int sample_cnt = 0; + for ( int x = 0; x < pool_w; ++x) { + avg += min_y[x]; + } + if (0) + { + double h = pool_h - (double)avg / pool_w; + score += int(ai_param.miny_factor * h * h / pool_h); + } + else + { + int h = std::min(std::min(min_y[gem_beg_x], min_y[gem_beg_x+1]), std::min(min_y[gem_beg_x+2], min_y[gem_beg_x+3])); + if ( h < 8 ) + { + score += int(ai_param.miny_factor * ( 8 - h ) * 2); + } + } + if (1) + { + if ( avg < pool_w * center ) { + warning_factor = 0.0 + (double)avg / pool_w / center / 1; + } + } + // Æ«²îÖµ + { + int dif_sum = 0; + for ( int x = 0; x < pool_w; ++x) { + dif_sum += abs( min_y[x] * pool_w - avg ); + } + score += ai_param.dif_factor * dif_sum / pool_w / pool_w; + } + } + // ¹¥»÷¼ÆËã + { + int s = 0; + int t_att = total_clear_att; + double t_clear = total_clears; //+ total_clears / 4.0; + if ( pool.b2b ) s -= 5; // b2b score + if ( t_clear > 0 ) { + s -= int( ((ai_param.clear_efficient) * ( t_att ) ) ); + } + { + //if ( t_clear > t_att ) { + //int warning_factor = 0.5 + (double)avg / pool_w / center / 2; + s += int( warning_factor * t_clear * ai_param.clear_useless_factor); + //} + } + int cs = 0; + if ( cur_num == GEMTYPE_T && wallkick_spin && clears > 0 && ai_param.tspin > 0 ) { // TÏû¸½¼Ó·Ö£¬Òª±ÈT1/T2ÐÎ×´»ù±¾·Ö´óÒ» + s -= ai_param.hold_T; + if ( clears >= 3 ) { + if ( clear_att >= clears * 2 ) { // T3 + cs -= int( warning_factor * (ai_param.tspin3 * 8 ) + ai_param.hole * 2 ); + } + } else if ( clears >= 2 ) { + if ( clear_att >= clears * 2 ) { // T2 + cs -= int( warning_factor * (ai_param.tspin * 5 + ai_param.open_hole / 2) ); + } + } else if ( wallkick_spin == 1 ) { // T1 + cs -= int( warning_factor * (ai_param.tspin * 1 + ai_param.open_hole / 2) ); + } else if ( wallkick_spin == 2 ) { // Tmini + cs -= int( warning_factor * (ai_param.tspin / 2) ); + } + } + clearScore += cs; +#ifdef XP_RELEASE + if (1) + if ( clears > 0 && upcomeAtt >= 4 && ai_param.upcomeAtt > 0 ) { + int cur_s = 0; + cur_s -= int( ((ai_param.clear_efficient) * ( clear_att ) ) ) + - int( warning_factor * clears * ai_param.clear_useless_factor); + if ( avg - (12 + upcomeAtt) * pool_w > 0 && cur_s + cs < 0 ) + s -= (cur_s + cs) * ( avg - (12 + upcomeAtt) * pool_w ) * ai_param.upcomeAtt / pool_w / 10 / 20; + //if ( upcomeAtt >= 4 ) { + // if ( total_hole < 4 && avg - upcomeAtt * pool_w >= 8 * pool_w ) { + // s = s - s * ( 4 - total_hole ) * ai_param.upcomeAtt / 40; + // } + //} + } +#endif + score += s; + } + //if ( clears ) { + // int center = 10; // °ÚÂ¥¾¯½äÏß + // double factor = 1; + // if ( avg < pool_w * center ) { + // factor = (double)avg / pool_w / center; + // } + // int s = 0; + // if ( pool_hole_score < last_pool_hole_score ) { + // s -= int( factor * (ai_param.clear_efficient * ( clear_att ) * ( clear_att ) / clears) ); + // //s -= ai_param.open_hole; + // if ( clear_att >= 4 ) { + // if ( clear_att >= clears * 2 ) { // T2/T3 + // clearScore -= int( factor * (ai_param.tspin * 4 + ai_param.open_hole + ai_param.clear_efficient * ( clear_att ) ) ); + // s -= ai_param.hold_T; + // } + // } + // if ( clears > clear_att ) { + // s += int( factor * (ai_param.clear_efficient * ( clears - clear_att ) / 2 ) ); + // } + // } else if ( pool_hole_score == last_pool_hole_score ) { + // s -= int( factor * (ai_param.clear_efficient * ( clear_att ) * ( clear_att ) / clears) ); + // if ( clear_att >= 4 ) { + // if ( clear_att >= clears * 2 ) { // T2/T3 + // clearScore -= int( factor * (ai_param.tspin * 4 + ai_param.open_hole + ai_param.clear_efficient * ( clear_att ) ) ); + // s -= ai_param.hold_T; + // } + // } else if ( clear_att >= clears ) { + // if ( clear_att >= clears * 2 ) { + // if ( clears == 1 ) { // T1 + // //s += int( factor * (ai_param.clear_efficient * ( clear_att ) / clears) ); + // } + // } + // } else if ( avg < 8 * pool_w ) { + // //s += int(ai_param.hole * ( clears - clear_att ) * factor / 2 ); + // if ( clears > clear_att ) { + // s += int( factor * (ai_param.clear_efficient * ( clears - clear_att ) / 2 ) ); + // } + // } else if ( total_hole >= 1 || min_y[maxy_index] < pool_h - 4 ) { + // if ( clears > clear_att ) { + // s += int( factor * (ai_param.clear_efficient * ( clears - clear_att ) * 2 ) ); + // } + // //if ( clear_att == 0 ) { + // // s += int( factor * (ai_param.hole * ( clears - clear_att ) ) / 3 ); + // //} + // } else { + // if ( clears > clear_att ) { + // s += int( factor * (ai_param.clear_efficient * ( clears - clear_att ) * 4) ); + // } + // //if ( clear_att == 0 ) { + // // s += int( factor * (ai_param.hole * ( clears - clear_att ) ) / 3 ); + // //} + // } + // } else { + // s -= int( factor * (ai_param.clear_efficient * ( clear_att ) / clears) ); + // if ( clears > clear_att ) { + // s += int( factor * (ai_param.clear_efficient * ( clears - clear_att ) * 4 ) ); + // } + // } + // if ( pool.combo > 2 ) + // { + // int combo = pool.combo - 2; + // //clearScore -= combo * combo * ai_param.combo_factor; + // } + // score += s; + //} + } + + // ÌØÊâÐÎ×´Åж¨ + + // ¼ÆËã¿É¹¥»÷£¨TetrisºÍT2£© + //int t2_x[32] = {0}; + if ( maxy_cnt == 0 ) + { + //if ( maxy_index == 0 || maxy_index == pool_w - 1 ) { + // score += ai_param.att_col_sel_side; + //} + int ybeg = 0; + if ( softdropEnable() && maxy_index > 0 && maxy_index < pool_w - 1 && ai_param.tspin > 0 ) { // T1/T2»ù±¾ÐÎ×´·Ö + ybeg = std::max( min_y[maxy_index - 1], min_y[maxy_index + 1] ); + if ( min_y[maxy_index - 1] == min_y[maxy_index + 1] + && x_holes[ybeg] == 0 && x_holes[ybeg-1] == 0 + && x_op_holes[ybeg] == 0 && x_op_holes[ybeg-1] == 0 + ) + { // T×¼±¸ + int cnt = 0; + if ( maxy_index > 1 && min_y[maxy_index - 2] >= min_y[maxy_index - 1] - 2 ) ++cnt; + if ( maxy_index < pool_w - 2 && min_y[maxy_index + 2] >= min_y[maxy_index + 1] - 2 ) ++cnt; + if ( cnt > 0 ) + { + score -= int(warning_factor * ai_param.tspin); + if ( (~pool.row[ybeg] & pool.m_w_mask) == (1 << maxy_index) ) { // T1»ù´¡ + score -= int(warning_factor * ai_param.tspin); + if ( (~pool.row[ybeg - 1] & pool.m_w_mask) == (7 << (maxy_index-1) ) ) { // ¿ÉT2ÍêÃÀ¿Ó + score -= int( warning_factor * (ai_param.tspin * cnt) ); + } + } + } + } else if ( ybeg <= 6 && ybeg - t_dis > 1 || ybeg > 6 ) { + int row_data = pool.row[ybeg - 1]; + if ( (row_data & ( 1 << (maxy_index-1) ) ) == 0 && (row_data & ( 1 << (maxy_index+1) ) ) == 0 // ¿ÓµÄ×óÓÒΪ¿Õ + && x_holes[ybeg] == 0 && x_holes[ybeg-1] == 0 // ÆäËüλÖÃÎÞ¶´ + && x_op_holes[ybeg] == 0 && x_op_holes[ybeg-1] <= 1 + ) + { + // T¿ÓÐÎ×´ + if ( ( pool.row[ybeg] & (1 << (maxy_index-1)) ) && ( pool.row[ybeg] & (1 << (maxy_index+1)) ) ) { // ¿ÓµÄÏÂÃæÁ½¿é´æÔÚ + if ( !!( pool.row[ybeg-2] & (1 << (maxy_index-1)) ) + !!( pool.row[ybeg-2] & (1 << (maxy_index+1)) ) == 1 ) { // ¿ÓµÄÉÏÃæµÄ¿é´æÔÚ + double s = 0; + //t2_x[maxy_index] = ybeg; + double factor = ybeg > 6 ? 0.5 : 1 - t_dis / 6.0 * 0.5; + if ( warning_factor < 1 ) + factor = ybeg > 6 ? 1.0 / 5 : 1 / (1 + t_dis / 3.0); + s += ai_param.open_hole; + if ( (~pool.row[ybeg] & pool.m_w_mask) == (1 << maxy_index) ) { // ¿ÉT1 + s += ai_param.tspin + ai_param.tspin * 1 * factor; + if ( (~row_data & pool.m_w_mask) == (7 << (maxy_index-1) ) ) { // ¿ÉT2ÍêÃÀ¿Ó + s += ai_param.tspin * 3 * factor; + // s -= ai_param.tspin * 3 / factor / 1; + } + } else { + s += ai_param.tspin * 1 + ai_param.tspin * 2 * factor / 2 ; + } + score -= int( warning_factor * s ); + } + } + } + } + } else { + if ( maxy_index == 0 ) { + ybeg = min_y[maxy_index + 1]; + } else { + ybeg = min_y[maxy_index - 1]; + } + } + int readatt = 0; + int last = pool.row[ybeg]; + for ( int y = ybeg; y <= pool_h; ++y ) { + if ( last != pool.row[y] ) break; + int row_data = ~pool.row[y] & pool.m_w_mask; + if ( (row_data & (row_data - 1)) != 0 ) break; + ++readatt; + } + if ( readatt > 4 ) readatt = 4; + //score -= readatt * ai_param.readyatt; + + } + // T3 ÐÎ×´Åж¨ + //3001 + //2000 + // 1101 + // 1x01 + // 1101 + // + // 1003 + // 0002 + //1011 + //10x1 + //1011 + if ( softdropEnable() && ai_param.tspin3 > 0 ) + { + for ( int y = 3; y < pool_h; ++y ) { + if ( x_holes[y] == 0 ) continue; + for ( int x = 1; x < pool_w - 1; ++x ) { + if ( ( pool.row[y + 1] & ( 1 << x ) ) == 0 || ( pool.row[y + 1] & ( 1 << x ) ) == 0 ) { + continue; // ÉÏÏÂÎÞ¶´ + } + int row_y[5]; + for ( int i = 0; i < 5; ++i ) { + row_y[i] = ( (pool.row[y - 3 + i] | (3 << pool_w)) << 2 ) | 3; + } + if ( ( (row_y[3] >> (x + 1)) & ( 7 ) ) == 1 /*100*/ ) { // ÉÏͼÇé¿ö + if ( x == pool_w - 2 ) continue; + //if ( t2_x[x+1] == y ) continue; // ÅųýT2¿Ó + // ËùÓпյĵط½ÏÈÆ¥Åä + if ( ( (row_y[2] >> (x + 1)) & ( 7 ) ) != 3 /*110*/ + //|| ( (row_y[4] >> (x + 1)) & ( 15 ) ) != 11 /*1101*/ + || ( (row_y[4] >> (x + 1)) & ( 13 ) ) != 9 /*1011mask=1001*/ + || ( (row_y[1] >> (x + 1)) & ( 7 ) ) != 0 /*000*/ + //|| ( (row_y[0] >> (x + 1)) & ( 3 ) ) != 0 /*00*/ + ) { + continue; + } + if ( min_y[x] != y - 1 || min_y[x-1] != y - 1 ) { + continue; + } + if ( ( row_y[0] & ( 1 << (x) ) ) == 0 && ( row_y[1] & ( 1 << (x) ) ) ) { + continue; // ¸ß´¦×ª½Ç + } + if ( min_y[x + 1] > y ) { // ¶´Åж¨ + if ( x_holes[y - 1] > 0 || x_holes[y + 1] > 0 || x_holes[y] > 1 + || x_op_holes[y - 1] > 0 || x_op_holes[y + 1] > 0 || x_op_holes[y] > 0) + { + continue; + } + } else { + if ( x_holes[y - 1] > 1 || x_holes[y + 1] > 1 || x_holes[y] > 2 + || x_op_holes[y - 1] > 0 || x_op_holes[y + 1] > 0 || x_op_holes[y] > 0) + { + continue; + } + } + if ( ( (row_y[0] >> (x + 3)) & ( 1 ) ) == 0 && y - min_y[x + 2] > 3 ) continue; + int s = 0; + //tp3 * 1 + s -= int( warning_factor * ai_param.tspin3 );// + int( warning_factor * ( ai_param.tspin * 4 + ai_param.open_hole ) ); + score += s; + if ( y <= pool_h - 3 && ( pool.row[y + 3] & ( 1 << x ) ) == 0 ) { + int r = ~pool.row[y + 3] & pool.m_w_mask; + if ( ( r & ( r - 1 ) ) == 0 ) { + score -= int( warning_factor * (ai_param.tspin * 4 + ai_param.open_hole) ); + } + } + //int full = 0; + { + int e = ~(pool.row[y + 1] | (1<> (x+1) ) & ( 7 ) ) == 4 /*001*/ ) { // ¾µÏñÇé¿ö + if ( x == 1 ) continue; + //if ( t2_x[x-1] == y ) continue; // ÅųýT2¿Ó + // ËùÓпյĵط½ÏÈÆ¥Åä + if ( ( (row_y[2] >> (x+1)) & ( 7 ) ) != 6 /*011*/ + //|| ( (row_y[4] >> (x)) & ( 15 ) ) != 13 /*1011*/ + || ( (row_y[4] >> (x)) & ( 11 ) ) != 9 /*1101mask=1001*/ + || ( (row_y[1] >> (x + 1)) & ( 7 ) ) != 0 /*000*/ + //|| ( (row_y[0] >> (x + 1)) & ( 3 ) ) != 0 /*00*/ + ) { + continue; + } + if ( min_y[x] != y - 1 || min_y[x+1] != y - 1 ) { + continue; + } + if ( ( row_y[0] & ( 1 << (x + 4) ) ) == 0 && ( row_y[1] & ( 1 << (x + 4) ) ) ) { + continue; // ¸ß´¦×ª½Ç + } + if ( min_y[x - 1] > y ) { // ¶´Åж¨ + if ( x_holes[y - 1] > 0 || x_holes[y + 1] > 0 || x_holes[y] > 1 + || x_op_holes[y - 1] > 0 || x_op_holes[y + 1] > 0 || x_op_holes[y] > 0) + { + continue; + } + } else { + if ( x_holes[y - 1] > 1 || x_holes[y + 1] > 1 || x_holes[y] > 2 + || x_op_holes[y - 1] > 0 || x_op_holes[y + 1] > 0 || x_op_holes[y] > 0) + { + continue; + } + } + if ( ( (row_y[0] >> (x + 1)) & ( 1 ) ) == 0 && y - min_y[x - 2] > 3 ) continue; + int s = 0; + // tp3 * 1 + s -= int( warning_factor * ai_param.tspin3 );// + int( warning_factor * ( ai_param.tspin * 4 + ai_param.open_hole ) ); + score += s; + if ( y <= pool_h - 3 && ( pool.row[y + 3] & ( 1 << x ) ) == 0 ) { + int r = ~pool.row[y + 3] & pool.m_w_mask; + if ( ( r & ( r - 1 ) ) == 0 ) { + score -= int( warning_factor * (ai_param.tspin * 4 + ai_param.open_hole) ); + } + } + //int full = 0; + { + int e = ~(pool.row[y + 1] | (1< 0 && total_clears < 1 ) //&& lastCombo < 1 && pool.combo < 1 ) + { + int maxy_4w = min_y[3]; + maxy_4w = std::max(maxy_4w, min_y[4] ); + maxy_4w = std::max(maxy_4w, min_y[5] ); + maxy_4w = std::max(maxy_4w, min_y[6] ); + int maxy_4w_combo = min_y[0]; + maxy_4w_combo = std::max(maxy_4w_combo, min_y[1] ); + maxy_4w_combo = std::max(maxy_4w_combo, min_y[2] ); + maxy_4w_combo = std::max(maxy_4w_combo, min_y[pool_w-3] ); + maxy_4w_combo = std::max(maxy_4w_combo, min_y[pool_w-2] ); + maxy_4w_combo = std::max(maxy_4w_combo, min_y[pool_w-1] ); + if ( (min_y[4] < min_y[3] && min_y[4] <= min_y[5]) + || (min_y[5] < min_y[6] && min_y[5] <= min_y[4]) ) + { + maxy_4w = -10; + } else + for ( int x = 0; x < pool_w; ++x ) { + if ( min_y[x] > maxy_4w ) { + maxy_4w = -10; + break; + } + } + while ( maxy_4w > 0 ) { + //if ( abs( min_y[0] - min_y[1] ) > 4 ) { maxy_4w = -10; break; } + //if ( abs( min_y[1] - min_y[2] ) > 4 ) { maxy_4w = -10; break; } + //if ( abs( min_y[pool_w-1] - min_y[pool_w-2] ) > 4 ) { maxy_4w = -10; break; } + //if ( abs( min_y[pool_w-2] - min_y[pool_w-3] ) > 4 ) { maxy_4w = -10; break; } + //if ( abs( min_y[2] - min_y[pool_w-3] ) > 7 ) { maxy_4w = -10; break; } + //int avg = (min_y[0] + min_y[1] + min_y[2] + min_y[pool_w-1] + min_y[pool_w-2] + min_y[pool_w-3]) / 6; + if ( (pool_h - maxy_4w) * 2 >= maxy_4w - maxy_4w_combo ) { + maxy_4w = -10; + break; + } + break; + } + if ( maxy_4w <= pool_h - 4 ) { // Èç¹ûÓг¬¹ý4¹¥»÷ÐоͲ»´î + maxy_4w = -10; + } + //if ( maxy_4w - maxy_4w_combo > 15 ) { // Èç¹ûÓг¬¹ý10Ô¤±¸ÐоͲ»´î + // maxy_4w = -10; + //} + if ( maxy_4w - maxy_4w_combo < 9 && pool_hole_score > ai_param.hole * (maxy_4w - maxy_4w_combo) / 2 ) { + maxy_4w = -10; + } + + if ( maxy_4w > 8 ) { + bool has_hole = false; + for ( int y = maxy_4w - 1; y >= 0; --y ) { + if ( x_holes[y] || x_op_holes[y] ) { + has_hole = true; + break; + } + } + if ( ! has_hole && maxy_4w < pool_h ) { + if ( x_holes[maxy_4w]>1 || x_op_holes[maxy_4w]>1 ) { + has_hole = true; + } + } + + if ( ! has_hole ) + { + int sum = maxy_4w - min_y[3]; + sum += maxy_4w - min_y[4]; + sum += maxy_4w - min_y[5]; + sum += maxy_4w - min_y[6]; + int s = 0; + if ( sum == 3 || sum == 0 || sum == 4 ) //{ // - (pool_h - maxy_4w) - clears * lastCombo * 2 + { + int hv = (maxy_4w - maxy_4w_combo + 1) * 1 + pool.combo; + s += ai_param.strategy_4w * ( hv ) + (ai_param.hole * 2 + ai_param.tspin * 4); + if ( sum > 0 ) { + s -= ai_param.strategy_4w / 3; + } + } + if ( s > 0 ) { + score -= s; + } + //if ( pool_h * 4 + 4 + x_holes[pool_h] + x_op_holes[pool_h] - min_y[0] - min_y[1] - min_y[2] - min_y[3] <= 4 ) { + // score -= 800 + (ai_param.hole * 2 + ai_param.tspin * 4); + //} else if ( pool_h * 4 + 4 + x_holes[pool_h] + x_op_holes[pool_h] - min_y[pool_w - 4] - min_y[pool_w - 3] - min_y[pool_w - 2] - min_y[pool_w - 1] <= 4 ) { + // score -= 800 + (ai_param.hole * 2 + ai_param.tspin * 4); + //} + } + } + } + // ÀÛ»ý·Ö + score += clearScore; + return score; + } + struct MovsState { + MovingSimple first; + GameField pool_last; + int att, clear; + signed short max_combo, max_att, combo; + signed short player, upcomeAtt; + MovsState() { upcomeAtt = max_combo = combo = att = clear = 0; } + bool operator < (const MovsState& m) const { +#if 0 + { + if ( max_combo > (combo - 1) * 32 && m.max_combo > (m.combo - 1) * 32 ) { + if ( att > 8 || m.att > 8 ) { + if ( abs(first.score - m.first.score) < 400 ) { + if ( att != m.att ) + return att < m.att; + } else { + return first < m.first; + } + } + } + if ( ( max_combo > 6 * 32 || m.max_combo > 6 * 32 ) ) { + if ( max_combo != m.max_combo ) { + return max_combo < m.max_combo; + } + } + if ( ai_settings[player].strategy_4w ) + if ( ( combo > 3 * 32 || m.combo > 3 * 32 ) ) { + if ( combo != m.combo ) { + return combo < m.combo; + } + } + } + //if (0) + if ( (pool_last.combo > 3 * 32 || m.pool_last.combo > 3 * 32) && pool_last.combo != m.pool_last.combo) { + return pool_last.combo < m.pool_last.combo; + } +#else + //if ( abs(first.score - m.first.score) >= 900 ) { + // return first < m.first; + //} + //if ( (max_att >= 6 || m.max_att >= 6) && abs(max_att - m.max_att) >= 2 ) { + // return max_att < m.max_att; + //} + //else + if ( ai_settings[player].strategy_4w ) + { + if ( ( max_combo > 6 * 32 || m.max_combo > 6 * 32 ) ) { + if ( max_combo != m.max_combo ) { + return max_combo < m.max_combo; + } + } + if ( (combo >= 32 * 3 || m.combo >= 32 * 3) && combo != m.combo) { + return combo < m.combo; + } + } + else + { + if ( ai_settings[player].combo ) { + if ( ( max_combo > 6 * 32 || m.max_combo > 6 * 32 ) ) { + if ( max_combo != m.max_combo ) { + return max_combo < m.max_combo; + } + } + if ( max_combo > combo && m.max_combo > m.combo && (m.max_combo > 4 * 32 || max_combo > 4 * 32) ) { + if ( (combo <= 2 * 32 && m.combo <= 2 * 32) ) { + if ( abs(first.score - m.first.score) < 1000 ) { + if ( att != m.att ) + return att < m.att; + } else { + return first < m.first; + } + } + } + ////if ( ai_settings[player].strategy_4w ) { + // if ( ( combo > 3 * 32 || m.combo > 3 * 32 ) ) { + // if ( combo != m.combo ) { + // return combo < m.combo; + // } + // } + //} + } + //if (0) + //if ( (pool_last.combo > 32 || m.pool_last.combo > 32 ) ) + //{ + // int m1 = (max_combo!=pool_last.combo ? std::max(max_combo - 32 * 2, 0) * 2 : 0 ) + pool_last.combo; + // int m2 = (m.max_combo!=m.pool_last.combo ? std::max(m.max_combo - 32 * 2, 0) * 2 : 0 ) + m.pool_last.combo; + // if ( m1 != m2 ) { + // return m1 < m2; + // } + //} + if ( (combo > 32 * 2 || m.combo > 32 * 2) && combo != m.combo) { + return combo < m.combo; + } + } + //if ( (pool_last.combo > 1 || m.pool_last.combo > 1) && pool_last.combo != m.pool_last.combo) { + // return pool_last.combo < m.pool_last.combo; + //} +#endif + return first < m.first; + } + }; + struct GameState{ + uint64 hash; + signed short hold, att, clear, combo, b2b; + GameState(uint64 _hash + ,signed short _hold + ,signed short _att + ,signed short _clear + ,signed short _combo + ,signed short _b2b + ) + :hash(_hash) + ,hold(_hold) + ,att(_att) + ,combo(_combo) + ,b2b(_b2b) + { + } + bool operator < ( const GameState& gs) const { + if ( hash != gs.hash ) return hash < gs.hash; + if ( hold != gs.hold ) return hold < gs.hold; + if ( att != gs.att ) return att < gs.att; + if ( clear != gs.clear ) return clear < gs.clear; + if ( combo != gs.combo ) return combo < gs.combo; + if ( b2b != gs.b2b ) return b2b < gs.b2b; + return false; + } + bool operator == ( const GameState& gs) const { + if ( hash != gs.hash ) return false; + if ( hold != gs.hold ) return false; + if ( att != gs.att ) return false; + if ( clear != gs.clear ) return false; + if ( combo != gs.combo ) return false; + if ( b2b != gs.b2b ) return false; + return true; + }; + }; +#define BEG_ADD_Y 1 + MovingSimple AISearch(AI_Param ai_param, const GameField& pool, int hold, Gem cur, int x, int y, const std::vector& next, bool canhold, int upcomeAtt, int maxDeep, int & searchDeep, int level, int player) { + assert( cur.num != 0 ); + typedef std::vector MovingList; + MovingList movs; + MovQueue que(16384); + MovQueue que2(16384); + movs.reserve(128); + int search_nodes = 0; + const int combo_step_max = 32; + searchDeep = 0; + upcomeAtt = std::min(upcomeAtt, pool.height() - gem_beg_y - 1); + if ( pool.combo > 0 && (pool.row[10] || pool.combo > 1) ) ai_param.strategy_4w = 0; + if ( ai_param.hole < 0 ) ai_param.hole = 0; + ai_param.hole += ai_param.open_hole; +#if AI_WEAK_VERSION || defined(XP_RELEASE) + int ai_level_map[] = { + 4000, //LV0 search all + 4000, //LV1 search all + 4000, + 4000, + 4000, //lv4 + 5000, + 6000, + 8000, //LV7 + 16000, + 32000, + 64000, + }; + int max_search_nodes = ai_level_map[level]; + //if ( AI_SHOW && GAMEMODE_4W ) max_search_nodes *= 2; + if ( level <= 0 ) maxDeep = 0; + else if ( level <= 6 ) maxDeep = std::min(level, 6); // TODO: max deep + //else maxDeep = level; +#else + int max_search_nodes = 4000; +#endif + int next_add = 0; + if ( pool.m_hold == 0 ) { + next_add = 1; + if ( next.empty() ) { + return MovingSimple(); + } + } + + { + const GameField& _pool = pool; + int t_dis = 14; + if ( _pool.m_hold != GEMTYPE_T ) { + for ( size_t i = 0; i < next.size(); ++i ) { + if ( next[i].num == GEMTYPE_T ) { + t_dis = i; + break; + } + } + } else { + t_dis = 0; + } + GenMoving(_pool, movs, cur, x, y, 0); + for (MovingList::iterator it = movs.begin(); it != movs.end(); ++it) { + ++search_nodes; + MovsState &ms = que.append(); + ms.pool_last = _pool; + signed char wallkick_spin = it->wallkick_spin; + wallkick_spin = ms.pool_last.WallKickValue(cur.num, (*it).x, (*it).y, (*it).spin, wallkick_spin); + ms.pool_last.paste((*it).x, (*it).y, getGem(cur.num, (*it).spin)); + int clear = ms.pool_last.clearLines( wallkick_spin ); + int att = ms.pool_last.getAttack( clear, wallkick_spin ); + ms.player = player; + ms.clear = clear; + ms.att = att; + if ( clear > 0 ) { + ms.combo = _pool.combo * combo_step_max + combo_step_max;// + 1 - clear; + ms.upcomeAtt = std::max(0, upcomeAtt - att); + } else { + ms.combo = 0; + ms.upcomeAtt = -upcomeAtt; + ms.pool_last.minusRow(upcomeAtt); + } + ms.max_att = att; + ms.max_combo = ms.combo; //ms_last.max_combo + getComboAttack( ms.pool_last.combo ); + ms.first = *it; + ms.first.score2 = 0; + ms.first.score = Evaluate(ms.first.score2, ai_param, pool, ms.pool_last, cur.num, 0, ms.att, ms.clear, att, clear, wallkick_spin, _pool.combo, t_dis, upcomeAtt); + if ( wallkick_spin == 0 && it->wallkick_spin ) ms.first.score += 1; + que.push_back(); + } + } + if ( canhold && ! hold && + ( + pool.m_hold == 0 + && !next.empty() && ! pool.isCollide(gem_beg_x, gem_beg_y, getGem( next[0].num, 0 ) ) + || ! pool.isCollide(gem_beg_x, gem_beg_y, getGem( pool.m_hold, 0 ) ) + ) + ) + { + int cur_num; + if ( pool.m_hold ) { + cur_num = pool.m_hold; + } else { + cur_num = next[0].num; + } + if ( cur_num != cur.num ) { + GameField _pool = pool; + _pool.m_hold = cur.num; + int t_dis = 14; + if ( _pool.m_hold != GEMTYPE_T ) { + for ( size_t i = 0; i + next_add < next.size(); ++i ) { + if ( next[i + next_add].num == GEMTYPE_T ) { + t_dis = i; + break; + } + } + } else { + t_dis = 0; + } + int x = gem_beg_x, y = gem_beg_y; + Gem cur = getGem( cur_num, 0 ); + GenMoving(_pool, movs, cur, x, y, 1); + for (MovingList::iterator it = movs.begin(); it != movs.end(); ++it) { + ++search_nodes; + MovsState &ms = que.append(); + ms.pool_last = _pool; + signed char wallkick_spin = it->wallkick_spin; + wallkick_spin = ms.pool_last.WallKickValue(cur_num, (*it).x, (*it).y, (*it).spin, wallkick_spin); + ms.pool_last.paste((*it).x, (*it).y, getGem(cur_num, (*it).spin)); + int clear = ms.pool_last.clearLines( wallkick_spin ); + int att = ms.pool_last.getAttack( clear, wallkick_spin ); + ms.player = player; + ms.clear = clear; + ms.att = att; + if ( clear > 0 ) { + ms.combo = _pool.combo * combo_step_max + combo_step_max;// + 1 - clear; + ms.upcomeAtt = std::max(0, upcomeAtt - att); + } else { + ms.combo = 0; + ms.upcomeAtt = -upcomeAtt; + ms.pool_last.minusRow(upcomeAtt); + } + ms.max_att = att; + ms.max_combo = ms.combo; //ms_last.max_combo + getComboAttack( ms.pool_last.combo ); + ms.first = *it; + ms.first.score2 = 0; + ms.first.score = Evaluate(ms.first.score2, ai_param, pool, ms.pool_last, cur.num, 0, ms.att, ms.clear, att, clear, wallkick_spin, _pool.combo, t_dis, upcomeAtt); + if ( wallkick_spin == 0 && it->wallkick_spin ) ms.first.score += 1; + que.push_back(); + } + } + } + if ( que.empty() ) { + return MovingSimple(); + } + int sw_map1[16][8] = { + {999, 4, 2, 2, 2, 2, 2, 2}, + {999, 4, 4, 2, 2, 2, 2, 2}, + { 50, 999, 4, 2, 2, 2, 2, 2}, + { 20, 40, 999, 4, 2, 2, 2, 2}, + { 15, 30, 20, 999, 2, 2, 2, 2}, // 4 + { 13, 25, 15, 12, 999, 2, 2, 2}, + { 14, 27, 17, 14, 20, 999, 3, 2}, + //{ 15, 27, 17, 15, 20, 999, 3, 2}, + //{ 20, 30, 20, 20, 20, 100, 999, 999}, + { 20, 30, 25, 20, 20, 100, 999, 999}, // 7 + { 25, 60, 50, 40, 40, 40, 500, 999}, + //{ 30, 50, 40, 30, 30, 25, 25, 20}, + //{ 30, 150, 130, 130, 110, 100, 100, 80}, + { 30, 90, 75, 60, 60, 60, 60, 9999}, // 9 + //{ 50, 720, 720, 480, 480, 480, 480, 480}, // 9 PC + //{ 30, 90, 80, 60, 60, 60, 60, 60}, + { 30, 240, 200, 200, 180, 160, 160, 9999}, // 10 + }; + int sw_map2[16][8] = { + {999, 999, 999, 999, 999, 999, 999, 999}, + { 60, 60, 999, 999, 999, 999, 999, 999}, + { 40, 40, 40, 999, 999, 999, 999, 999}, + { 30, 60, 60, 60, 999, 999, 999, 999}, + { 25, 45, 30, 30, 30, 999, 999, 999}, // 4 + { 25, 35, 35, 30, 30, 30, 999, 999}, + { 25, 35, 35, 35, 30, 25, 25, 999}, + { 25, 45, 40, 30, 30, 30, 30, 30}, // 7 + { 25, 90, 80, 60, 50, 50, 50, 50}, + //{ 30, 220, 200, 200, 160, 150, 150, 120}, + { 30, 150, 130, 100, 80, 80, 50, 50}, // 9 + //{ 30, 150, 130, 130, 130, 130, 130, 130}, // 9 PC + { 30, 300, 200, 180, 120, 100, 80, 80}, // 10 + //{ 30, 400, 400, 300, 300, 300, 300, 200}, // 10 + }; + int sw_map3[16][8] = { + {999, 999, 999, 999, 999, 999, 999, 999}, + { 60, 60, 999, 999, 999, 999, 999, 999}, + { 40, 40, 40, 999, 999, 999, 999, 999}, + { 30, 60, 60, 60, 999, 999, 999, 999}, + { 25, 45, 30, 30, 30, 999, 999, 999}, // 4 + { 25, 35, 35, 30, 30, 30, 999, 999}, + { 25, 35, 35, 35, 30, 25, 25, 999}, + { 25, 45, 40, 30, 30, 30, 30, 30}, // 7 + { 25, 90, 80, 60, 50, 40, 30, 30}, + //{ 30, 220, 200, 200, 160, 150, 150, 120}, + { 30, 120, 100, 80, 70, 60, 50, 40}, // 9 + //{ 30, 150, 130, 130, 130, 130, 130, 130}, // 9 PC + { 30, 240, 200, 160, 120, 90, 70, 60}, // 10 + }; + MovQueue * pq_last = &que2, * pq = &que; + searchDeep = 1; + for ( int depth = 0; search_nodes < max_search_nodes && depth < maxDeep; searchDeep = ++depth ) { //d < maxDeep + std::swap(pq_last, pq); +#if defined(XP_RELEASE) + int (*sw_map)[8] = sw_map1; + if ( ai_settings[player].hash ) { + sw_map = sw_map2; + //if ( ai_param.strategy_4w > 0 ) { + // sw_map = sw_map3; + //} + } + int search_base_width = sw_map[level][0];// - sw_map[level][0] / 6; + int search_wide = 1000; + if ( depth > 7 ) search_wide = sw_map[level][7]; + else search_wide = sw_map[level][depth]; +#else + int sw_map[16][8] = { + {15, 30, 20, 15, 10, 10, 10, 10}, + }; + int search_wide = 0; + if ( depth > 7 ) search_wide = sw_map[0][7]; + else search_wide = sw_map[0][depth]; + int search_base_width = sw_map[0][0];; +#endif + //int seach_select_best = (level <= 3 ? 1000 : (std::min(search_wide, 30) ) ); + int seach_select_best = std::min(search_wide - search_wide / 4, search_base_width); + if ( level <= 3 ) { + seach_select_best = search_wide - search_wide / 4; + } + if ( seach_select_best < search_base_width ) { + seach_select_best = std::min(search_base_width, std::max(15, search_wide) ); + } + pq->clear(); + int max_combo = 3; + int max_search_score = pq_last->back().first.score; + { + for ( int s = pq_last->size(), i = s / 2; i < s; ++i ) { + max_search_score = std::max( max_search_score, pq_last->queue[i].first.score ); + } + max_search_score = (max_search_score * 2 + pq_last->front().first.score) / 3; + } + std::set gsSet; + for ( int pqmax_size = (int)pq_last->size(), + pq_size = (int)pq_last->size(), + stop_size = std::max(0, (int)pq_size - search_wide); + pq_size > stop_size; + + --pq_size, pq_last->dec_size()) + { + + if ( pq_size > 1 ) pq_last->pop_back(); + + const MovsState &ms_last = pq_last->back(); + if ( pq_size != pqmax_size && ms_last.first.score > 50000000 ) { // ³¬¸ß·Ö¼ôÖ¦ + break; + } + if ( search_nodes >= max_search_nodes ) { + //MovsState ms_last = pq_last->back(); + pq->push(ms_last); + continue; + } + max_combo = std::max( max_combo, (int)ms_last.pool_last.combo ); + if (0) + if ( pq_size != pqmax_size ) { // ³¬¸ßcomboºóµÄÎÞcombo¼ôÖ¦ + if ( ms_last.pool_last.combo > 0 && max_combo > 5 && ms_last.pool_last.combo < max_combo - 1 ) { + break; + } + //if ( ms_last.pool_last.combo > 0 && max_combo > 3 ) { + // if ( max_combo - ms_last.pool_last.combo != 0 && max_combo - ms_last.pool_last.combo <= 1 ) { + // break; + // } + //} + } + if (0) + if ( depth > 0 && maxDeep > 2 && ms_last.first.score > max_search_score ) { + if ( pq_size + 2 < pqmax_size ) { + break; + } + } + if ( ai_settings[player].hash ) + { + GameState gs(ms_last.pool_last.hashval, ms_last.pool_last.m_hold, ms_last.att, ms_last.clear, ms_last.combo, ms_last.pool_last.b2b); + if ( gsSet.find(gs) == gsSet.end() ) { + gsSet.insert(gs); + } else { + continue; + } + } + int hold = 0; + //if ( !ms_last.first.movs.empty() && ms_last.first.movs[0] == Moving::MOV_HOLD ) hold = 1; + if ( !ms_last.first.hold ) hold = 1; + int t_dis = 14; + int d_pos = depth; + if ( next_add && ms_last.pool_last.m_hold ) d_pos = depth + 1; + if ( d_pos >= next.size() ) { + pq->push(ms_last); + continue; + } + int cur_num = next[d_pos].num; + if ( ms_last.pool_last.m_hold != GEMTYPE_T ) { + for ( size_t i = 0; d_pos + 1 + i < next.size(); ++i ) { + if ( next[d_pos + 1 + i].num == GEMTYPE_T ) { + t_dis = i; + break; + } + } + } else { + t_dis = 0; + } + if ( BEG_ADD_Y && ms_last.upcomeAtt < 0 ) + GenMoving(ms_last.pool_last, movs, getGem( cur_num, 0 ), AI::gem_beg_x, AI::gem_beg_y - ms_last.upcomeAtt, 0 ); + else + GenMoving(ms_last.pool_last, movs, getGem( cur_num, 0 ), AI::gem_beg_x, AI::gem_beg_y, 0 ); + if ( movs.empty() ) { + MovsState ms = ms_last; + ms.first.score += 100000000; + pq->push(ms); + continue; // ³öÏ־͹ҵĻ°Ê¹ÓÃholdµÄÇé¿ö²»ÄÜÅÜ + } else { + MovQueue p(movs.size()); + for (size_t i = 0; i < movs.size() ; ++i) { + ++search_nodes; + MovsState &ms = p.append(); + { + ms.first = ms_last.first; + ms.pool_last = ms_last.pool_last; + signed char wallkick_spin = movs[i].wallkick_spin; + wallkick_spin = ms.pool_last.WallKickValue(cur_num, movs[i].x, movs[i].y, movs[i].spin, wallkick_spin); + ms.pool_last.paste(movs[i].x, movs[i].y, getGem(cur_num, movs[i].spin)); + int clear = ms.pool_last.clearLines( wallkick_spin ); + int att = ms.pool_last.getAttack( clear, wallkick_spin ); + ms.player = player; + ms.clear = clear + ms_last.clear; + ms.att = att + ms_last.att; + ms.upcomeAtt = ms_last.upcomeAtt; + if ( clear > 0 ) { + ms.combo = ms_last.combo + combo_step_max + 1 - clear; + if ( ms_last.upcomeAtt > 0 ) + ms.upcomeAtt = std::max(0, ms_last.upcomeAtt - att); + } else { + ms.combo = 0; + if ( ms_last.upcomeAtt > 0 ) { + ms.upcomeAtt = -ms_last.upcomeAtt; + ms.pool_last.minusRow(ms_last.upcomeAtt); + } + } + ms.max_att = std::max((int)ms_last.max_att, ms_last.att + att); + ms.max_combo = std::max(ms_last.max_combo, ms.combo); //ms_last.max_combo + getComboAttack( ms.pool_last.combo ); + ms.first.score2 = ms_last.first.score2; + ms.first.score = Evaluate(ms.first.score2, ai_param, + pool, + ms.pool_last, cur_num, depth + 1, ms.att, ms.clear, att, clear, wallkick_spin, ms_last.pool_last.combo, t_dis, ms_last.upcomeAtt); + if ( wallkick_spin == 0 && movs[i].wallkick_spin ) ms.first.score += 1; + } + p.push_back(); + } + for ( int i = 0; i < seach_select_best && ! p.empty(); ++i) { + pq->push(p.front()); + p.pop_back(); + p.dec_size(); + } + } + if ( canhold && depth + next_add < next.size()) + { + MovsState ms_last = pq_last->back(); + //int cur_num = ms_last.pool_last.m_hold; + int cur_num; + int d_pos = depth + next_add; + if ( ms_last.pool_last.m_hold != next[d_pos].num ) { + if ( ms_last.pool_last.m_hold ) { + cur_num = ms_last.pool_last.m_hold; + } else { + cur_num = next[d_pos].num; + } + ms_last.pool_last.m_hold = next[d_pos].num; + if ( ms_last.pool_last.m_hold != GEMTYPE_T ) { + t_dis -= next_add; + if ( t_dis < 0 ) { + for ( size_t i = 0; d_pos + 1 + i < next.size(); ++i ) { + if ( next[i + 1 + d_pos].num == GEMTYPE_T ) { + t_dis = i; + break; + } + } + } + } else { + t_dis = 0; + } + if ( BEG_ADD_Y && ms_last.upcomeAtt < 0 ) + GenMoving(ms_last.pool_last, movs, getGem( cur_num, 0 ), AI::gem_beg_x, AI::gem_beg_y - ms_last.upcomeAtt, 1 ); + else + GenMoving(ms_last.pool_last, movs, getGem( cur_num, 0 ), AI::gem_beg_x, AI::gem_beg_y, 1 ); + if ( movs.empty() ) { + MovsState ms = ms_last; + ms.first.score += 100000000; + pq->push(ms); + } else { + MovQueue p(movs.size()); + for (size_t i = 0; i < movs.size() ; ++i) { + ++search_nodes; + MovsState &ms = p.append(); + { + ms.first = ms_last.first; + ms.pool_last = ms_last.pool_last; + signed char wallkick_spin = movs[i].wallkick_spin; + wallkick_spin = ms.pool_last.WallKickValue(cur_num, movs[i].x, movs[i].y, movs[i].spin, wallkick_spin); + ms.pool_last.paste(movs[i].x, movs[i].y, getGem(cur_num, movs[i].spin)); + int clear = ms.pool_last.clearLines( wallkick_spin ); + int att = ms.pool_last.getAttack( clear, wallkick_spin ); + ms.player = player; + ms.clear = clear + ms_last.clear; + ms.att = att + ms_last.att; + ms.upcomeAtt = ms_last.upcomeAtt; + if ( clear > 0 ) { + ms.combo = ms_last.combo + combo_step_max + 1 - clear; + if ( ms_last.upcomeAtt > 0 ) + ms.upcomeAtt = std::max(0, ms_last.upcomeAtt - att); + } else { + ms.combo = 0; + if ( ms_last.upcomeAtt > 0 ) { + ms.upcomeAtt = -ms_last.upcomeAtt; + ms.pool_last.minusRow(ms_last.upcomeAtt); + } + } + ms.max_att = std::max((int)ms_last.max_att, ms_last.att + att); + ms.max_combo = std::max(ms_last.max_combo, ms.combo); //ms_last.max_combo + getComboAttack( ms.pool_last.combo ); + ms.first.score2 = ms_last.first.score2; + ms.first.score = Evaluate(ms.first.score2, ai_param, + pool, + ms.pool_last, cur_num, depth + 1, ms.att, ms.clear, att, clear, wallkick_spin, ms_last.pool_last.combo, t_dis, ms_last.upcomeAtt); + if ( wallkick_spin == 0 && movs[i].wallkick_spin ) ms.first.score += 1; + } + p.push_back(); + } + for ( int i = 0; i < seach_select_best && ! p.empty(); ++i) { + pq->push(p.front()); + p.pop_back(); + p.dec_size(); + } + } + } + } + } + if ( pq->empty() ) { + return MovingSimple(); + } + } + //if (0) + if ( ai_settings[player].hash && canhold && search_nodes < max_search_nodes ) { // extra search + std::swap(pq_last, pq); + pq->clear(); + int depth = searchDeep - 1; +#if defined(XP_RELEASE) + int (*sw_map)[8] = sw_map1; + if ( ai_settings[player].hash ) + sw_map = sw_map2; + int search_base_width = sw_map[level][0];// - sw_map[level][0] / 6; + int search_wide = 1000; + if ( depth > 7 ) search_wide = sw_map[level][7]; + else search_wide = sw_map[level][depth]; +#else + int search_wide = (depth < 2 ? 20 : 20); + int search_base_width = 20; +#endif + //int seach_select_best = (level <= 3 ? 1000 : (std::min(search_wide, 30) ) ); + int seach_select_best = std::min(search_wide - search_wide / 4, search_base_width); + if ( level <= 3 ) { + seach_select_best = search_wide - search_wide / 4; + } + if ( seach_select_best < search_base_width ) { + seach_select_best = std::min(search_base_width, std::max(15, search_wide) ); + } + + std::set gsSet; + for ( int pqmax_size = (int)pq_last->size(), + pq_size = (int)pq_last->size(), + stop_size = std::max(0, (int)pq_size - search_wide); + pq_size > stop_size; + + --pq_size, pq_last->dec_size()) + { + + if ( pq_size > 1 ) pq_last->pop_back(); + + const MovsState &ms_last = pq_last->back(); + if ( pq_size != pqmax_size && ms_last.first.score > 50000000 ) { // ³¬¸ß·Ö¼ôÖ¦ + break; + } + pq->push(ms_last); + if ( search_nodes >= max_search_nodes ) { + continue; + } + //max_combo = std::max( max_combo, (int)ms_last.pool_last.combo ); + { + GameState gs(ms_last.pool_last.hashval, ms_last.pool_last.m_hold, ms_last.att, ms_last.clear, ms_last.combo, ms_last.pool_last.b2b); + if ( gsSet.find(gs) == gsSet.end() ) { + gsSet.insert(gs); + } else { + continue; + } + } + //if ( !ms_last.first.movs.empty() && ms_last.first.movs[0] == Moving::MOV_HOLD ) hold = 1; + if ( !ms_last.first.hold ) { + continue; + } + int t_dis = 14; + int d_pos = depth + next_add; + int cur_num = ms_last.pool_last.m_hold; + for ( size_t i = 0; d_pos + 1 + i < next.size(); ++i ) { + if ( next[d_pos + 1 + i].num == GEMTYPE_T ) { + t_dis = i; + break; + } + } + if ( BEG_ADD_Y && ms_last.upcomeAtt < 0 ) + GenMoving(ms_last.pool_last, movs, getGem( cur_num, 0 ), AI::gem_beg_x, AI::gem_beg_y - ms_last.upcomeAtt, 0 ); + else + GenMoving(ms_last.pool_last, movs, getGem( cur_num, 0 ), AI::gem_beg_x, AI::gem_beg_y, 0 ); + if ( movs.empty() ) { + MovsState ms = ms_last; + ms.first.score += 100000000; + pq->push(ms); + } else { + MovQueue p; + for (size_t i = 0; i < movs.size() ; ++i) { + ++search_nodes; + MovsState &ms = p.append(); + { + ms.first = ms_last.first; + ms.pool_last = ms_last.pool_last; + signed char wallkick_spin = movs[i].wallkick_spin; + wallkick_spin = ms.pool_last.WallKickValue(cur_num, movs[i].x, movs[i].y, movs[i].spin, wallkick_spin); + ms.pool_last.paste(movs[i].x, movs[i].y, getGem(cur_num, movs[i].spin)); + int clear = ms.pool_last.clearLines( wallkick_spin ); + int att = ms.pool_last.getAttack( clear, wallkick_spin ); + ms.player = player; + ms.clear = clear + ms_last.clear; + ms.att = att + ms_last.att; + ms.upcomeAtt = ms_last.upcomeAtt; + if ( clear > 0 ) { + ms.combo = ms_last.combo + combo_step_max + 1 - clear; + if ( ms_last.upcomeAtt > 0 ) + ms.upcomeAtt = std::max(0, ms_last.upcomeAtt - att); + } else { + ms.combo = 0; + if ( ms_last.upcomeAtt > 0 ) { + ms.upcomeAtt = -ms_last.upcomeAtt; + ms.pool_last.minusRow(ms_last.upcomeAtt); + } + } + ms.max_att = std::max((int)ms_last.max_att, ms_last.att + att); + ms.max_combo = std::max(ms_last.max_combo, ms.combo); //ms_last.max_combo + getComboAttack( ms.pool_last.combo ); + ms.first.score2 = ms_last.first.score2; + ms.first.score = Evaluate(ms.first.score2, ai_param, + pool, + ms.pool_last, cur_num, depth + 1, ms.att, ms.clear, att, clear, wallkick_spin, ms_last.pool_last.combo, t_dis, ms_last.upcomeAtt); + } + p.push_back(); + } + for ( int i = 0; i < seach_select_best && ! p.empty(); ++i) { + pq->push(p.front()); + p.pop_back(); + p.dec_size(); + } + } + } + if ( pq->empty() ) { + return MovingSimple(); + } + } + { + MovsState m, c; + std::swap(pq_last, pq); + pq_last->pop(m); + if ( ! GAMEMODE_4W ) { + while ( ! pq_last->empty() ) { + pq_last->pop(c); + if ( m.first.score > c.first.score ) { + m = c; + } + } + } + return m.first; + } + } + struct AI_THREAD_PARAM { + TetrisAI_t func; + Moving* ret_mov; + int* flag; + AI_Param ai_param; + GameField pool; + int hold; + Gem cur; + int x; + int y; + std::vector next; + bool canhold; + int upcomeAtt; + int maxDeep; + int *searchDeep; + int level; + int player; + AI_THREAD_PARAM(TetrisAI_t _func, Moving& _ret_mov, int& _flag, const AI_Param& _ai_param, const GameField& _pool, int _hold, Gem _cur, int _x, int _y, const std::vector& _next, bool _canhold, int _upcomeAtt, int _maxDeep, int & _searchDeep, int _level, int _player) { + func = _func; + ret_mov = &_ret_mov; + flag = &_flag; + ai_param = _ai_param; + pool = _pool; + hold = _hold; + cur = _cur; + x = _x; + y = _y; + next = _next; + canhold = _canhold; + upcomeAtt = _upcomeAtt; + maxDeep = _maxDeep; + searchDeep = &_searchDeep; + level = _level; + player = _player; + } + ~AI_THREAD_PARAM() { + } + }; + void AI_Thread( void* lpParam ) { + AI_THREAD_PARAM* p = (AI_THREAD_PARAM*)lpParam; + int searchDeep = 0; + *p->flag = 1; + + AI::MovingSimple best = AISearch(p->ai_param, p->pool, p->hold, p->cur, p->x, p->y, p->next, p->canhold, p->upcomeAtt, p->maxDeep, searchDeep, p->level, p->player); + AI::Moving mov; + const AI::GameField &gamefield = p->pool; + std::vector &gemNext = p->next; + mov.movs.push_back(Moving::MOV_DROP); + if ( best.x != AI::MovingSimple::INVALID_POS ) { // find path + int hold_num = gamefield.m_hold; + if ( gamefield.m_hold == 0 && ! gemNext.empty()) { + hold_num = gemNext[0].num; + } + std::vector movs; + AI::Gem cur; + if ( best.hold ) { + cur = AI::getGem(hold_num, 0); + FindPathMoving(gamefield, movs, cur, AI::gem_beg_x, AI::gem_beg_y, true); + } else { + cur = p->cur; + FindPathMoving(gamefield, movs, cur, p->x, p->y, false); + } + for ( size_t i = 0; i < movs.size(); ++i ) { + if ( movs[i].x == best.x && movs[i].y == best.y && movs[i].spin == best.spin ) { + if ( (isEnableAllSpin() || cur.num == GEMTYPE_T) ) { + if ( movs[i].wallkick_spin == best.wallkick_spin ) { + mov = movs[i]; + break; + } + } else { + mov = movs[i]; + break; + } + } else if ( cur.num == GEMTYPE_I || cur.num == GEMTYPE_Z || cur.num == GEMTYPE_S ) { + if ( (best.spin + 2 ) % 4 == movs[i].spin ) { + if ( best.spin == 0 ) { + if ( movs[i].x == best.x && movs[i].y == best.y - 1 ) { + mov = movs[i]; + break; + } + } else if ( best.spin == 1 ) { + if ( movs[i].x == best.x - 1 && movs[i].y == best.y ) { + mov = movs[i]; + break; + } + } else if ( best.spin == 2 ) { + if ( movs[i].x == best.x && movs[i].y == best.y + 1 ) { + mov = movs[i]; + break; + } + } else if ( best.spin == 3 ) { + if ( movs[i].x == best.x + 1 && movs[i].y == best.y ) { + mov = movs[i]; + break; + } + } + } + } + } + } + if ( mov.movs.empty() ) { + mov.movs.clear(); + mov.movs.push_back( AI::Moving::MOV_NULL ); + mov.movs.push_back( AI::Moving::MOV_DROP ); + } + *p->ret_mov = mov; + + *p->searchDeep = searchDeep; + *p->flag = -1; + delete p; + _endthread(); + } + int RunAI(Moving& ret_mov, int& flag, const AI_Param& ai_param, const GameField& pool, int hold, Gem cur, int x, int y, const std::vector& next, bool canhold, int upcomeAtt, int maxDeep, int & searchDeep, int level, int player) { + flag = 0; + _beginthread(AI_Thread, 0, new AI_THREAD_PARAM(NULL, ret_mov, flag, ai_param, pool, hold, cur, x, y, next, canhold, upcomeAtt, maxDeep, searchDeep, level, player) ); + return 0; + } + void AI_Thread_Dll( void* lpParam ) { + AI_THREAD_PARAM* p = (AI_THREAD_PARAM*)lpParam; + *p->flag = 1; + { + extern std::vector g_combo_attack; + const GameField& gamefield = p->pool; + int overfield[32]; + int field[32]; + char next[32]; + int comboTable[32]; + char gemMap[] = " ITLJZSO"; + for ( int iy = 0; iy <= gamefield.height(); ++iy ) { + field[iy] = gamefield.row[iy]; + } + for ( int iy = 0; iy < 16; ++iy ) { + overfield[iy] = gamefield.row[-iy-1]; + } + if ( p->maxDeep > 6 ) p->maxDeep = 6; + for ( int i = 0; i < p->maxDeep; ++i ) { + next[i] = gemMap[p->next[i].num]; + } + for ( int i = 0; i < g_combo_attack.size(); ++i ) { + comboTable[i] = g_combo_attack[i]; + if ( i + 1 == g_combo_attack.size() ) comboTable[i+1] = -1; + } + + char* pOutStr = p->func(overfield, field, gamefield.width(), gamefield.height(), gamefield.b2b, gamefield.combo, + next, gemMap[gamefield.m_hold], !p->hold, gemMap[p->cur.num], p->x, p->y, p->cur.spin, + true, spin180Enable(), p->upcomeAtt, comboTable, p->maxDeep, p->level, p->player); + + std::map outMap; + outMap[' '] = AI::Moving::MOV_NULL; + outMap['l'] = AI::Moving::MOV_L; + outMap['r'] = AI::Moving::MOV_R; + outMap['L'] = AI::Moving::MOV_LL; + outMap['R'] = AI::Moving::MOV_RR; + outMap['d'] = AI::Moving::MOV_D; + outMap['D'] = AI::Moving::MOV_DD; + outMap['z'] = AI::Moving::MOV_LSPIN; + outMap['x'] = AI::Moving::MOV_SPIN2; + outMap['c'] = AI::Moving::MOV_RSPIN; + outMap['v'] = AI::Moving::MOV_HOLD; + outMap['V'] = AI::Moving::MOV_DROP; + AI::Moving mov; + for ( ; *pOutStr; ++pOutStr ) { + mov.movs.push_back( outMap[*pOutStr] ); + } + *p->ret_mov = mov; + } + *p->flag = -1; + delete p; + _endthread(); + } + int RunAIDll(TetrisAI_t func, Moving& ret_mov, int& flag, const AI_Param& ai_param, const GameField& pool, int hold, Gem cur, int x, int y, const std::vector& next, bool canhold, int upcomeAtt, int maxDeep, int & searchDeep, int level, int player) { + flag = 0; + _beginthread(AI_Thread_Dll, 0, new AI_THREAD_PARAM(func, ret_mov, flag, ai_param, pool, hold, cur, x, y, next, canhold, upcomeAtt, maxDeep, searchDeep, level, player) ); + return 0; + } + +} diff --git a/tetris_ai/tetris_ai.h b/tetris_ai/tetris_ai.h new file mode 100644 index 0000000..58945f0 --- /dev/null +++ b/tetris_ai/tetris_ai.h @@ -0,0 +1,250 @@ +#pragma once +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + +#include "gamepool.h" + +#include +#include + +namespace AI { + struct Moving + { + enum { + MOV_NULL, + MOV_L, + MOV_R, + MOV_LL, + MOV_RR, + MOV_D, + MOV_DD, + MOV_LSPIN, + MOV_RSPIN, + MOV_DROP, + MOV_HOLD, + MOV_SPIN2, + MOV_REFRESH, + }; + std::vector movs; + int x, y; + int score, score2; + signed char spin; + signed char wallkick_spin; + Moving () { wallkick_spin = 0; movs.reserve (16); } + Moving ( const Moving & m ) { + //movs.reserve (16); + movs = m.movs; + x = m.x; + y = m.y; + spin = m.spin; + score = m.score; + score2 = m.score2; + wallkick_spin = m.wallkick_spin; + } + Moving ( const Moving & m, int _spin ) { + movs.reserve (16); + movs = m.movs; + spin = _spin; + } + bool operator < (const Moving& _m) const { + return score > _m.score; + } + bool operator == (const Moving& _m) const { + if ( x != _m.x || y != _m.y ) return false; + if ( spin != _m.spin ) return false; + if ( wallkick_spin != _m.wallkick_spin ) return false; + return true; + } + }; + struct MovingSimple + { + enum { + MOV_NULL, + MOV_L, + MOV_R, + MOV_LL, + MOV_RR, + MOV_D, + MOV_DD, + MOV_LSPIN, + MOV_RSPIN, + MOV_DROP, + MOV_HOLD, + MOV_SPIN2, + MOV_REFRESH, + }; + enum { + INVALID_POS = -64, + }; + int x, y; + int lastmove; + int score, score2; + signed char spin; + signed char wallkick_spin; + bool hold; + MovingSimple () { x = INVALID_POS; wallkick_spin = 0; lastmove = MovingSimple::MOV_NULL; } + MovingSimple ( const Moving & m ) { + x = m.x; + y = m.y; + spin = m.spin; + score = m.score; + score2 = m.score2; + wallkick_spin = m.wallkick_spin; + if (m.movs.empty()) hold = false; + else hold = (m.movs[0] == MovingSimple::MOV_HOLD); + if (m.movs.empty()) lastmove = MovingSimple::MOV_NULL; + else lastmove = m.movs.back(); + } + MovingSimple ( const MovingSimple & m ) { + x = m.x; + y = m.y; + spin = m.spin; + score = m.score; + score2 = m.score2; + wallkick_spin = m.wallkick_spin; + hold = m.hold; + lastmove = m.lastmove; + } + MovingSimple ( const MovingSimple & m, int _spin ) { + lastmove = m.lastmove; + hold = m.hold; + spin = (signed char)_spin; + } + bool operator == (const MovingSimple& _m) const { + if ( x != _m.x || y != _m.y ) return false; + if ( spin != _m.spin ) return false; + if ( lastmove != _m.lastmove ) return false; + if ( hold != _m.hold ) return false; + if ( wallkick_spin != _m.wallkick_spin ) return false; + return true; + } + bool operator < (const MovingSimple& _m) const { + return score > _m.score; + } + }; + template + struct MovList + { + std::vector queue; + //T queue[1024]; + int beg, end; + MovList() { + beg = end = 0; + } + MovList( size_t size ) { + beg = end = 0; + //queue.resize( size ); + queue.reserve( size ); + } + void clear() { + beg = end = 0; + queue.clear(); + } + size_t size() const { + return end - beg; + } + T& back() { + return queue[end-1]; + } + void push(const T& mov) { + queue.push_back(mov); + //queue[end] = mov; + ++end; + } + bool empty () const { + return beg == end; + } + void pop(T& mov) { + mov = queue[beg++]; + } + }; + template + struct MovQueue + { + std::vector queue; + //GameField pool; + MovQueue() { + } + MovQueue( size_t size ) { + queue.reserve( size ); + } + void clear() { + queue.clear(); + } + size_t size() const { + return queue.size(); + } + T& front() { + return queue.front(); + } + T& back() { + return queue.back(); + } + T& append() { + queue.push_back(T()); + return back(); + } + void push_back() { + std::push_heap(queue.begin(), queue.end()); + } + void pop_back() { + std::pop_heap(queue.begin(), queue.end()); + } + void dec_size() { + queue.pop_back(); + } + void push(const T& mov) { + queue.push_back(mov); + std::push_heap(queue.begin(), queue.end()); + } + bool empty () const { + return queue.empty(); + } + void pop(T& mov) { + std::pop_heap(queue.begin(), queue.end()); + mov = queue.back(); + queue.pop_back(); + } + void sort () { + std::sort( queue.begin(), queue.end() ); + } + }; + struct AI_Param { + int miny_factor; // ×î¸ß¸ß¶È·Ö + int hole; // -¶´·Ö + int open_hole; // -¿ª·Å¶´£¬¿ÉÄܲå¿é + int v_transitions; // -ˮƽת»»ÏµÊý + int tspin3; // T3»ù±¾·Ö + + int clear_efficient; // ÏûÐÐЧÂÊϵÊý + int upcomeAtt; // -Ô¤±¸¹¥»÷»ù±¾ÏµÊý + int h_factor; // -¸ß¶È²îϵÊý + int hole_dis_factor2; // -¶´¾àÀëϵÊý + int hole_dis; // -¶´µÄ¾àÀë·Ö + //int flat_factor; // ƽֱϵÊý + + int hole_dis_factor; // -¶´¾àÀëϵÊý + int tspin; // tspinϵÊý + int hold_T; // hold TºÍIϵÊý + int hold_I; // hold TºÍIϵÊý + int clear_useless_factor; // ÎÞЧÐÐϵÊý + //int ready_combo; // Á¬»÷Ô¤±¸·Öx + + int dif_factor; //Æ«²îÖµ + int strategy_4w; + }; + typedef char* (*AIName_t)( int level ); + typedef char* (*TetrisAI_t)(int overfield[], int field[], int field_w, int field_h, int b2b, int combo, + char next[], char hold, bool curCanHold, char active, int x, int y, int spin, + bool canhold, bool can180spin, int upcomeAtt, int comboTable[], int maxDepth, int level, int player); + void setComboList( std::vector combolist ); + int getComboAttack( int combo ); + void setSpin180( bool enable ); + bool spin180Enable(); + void setAIsettings(int player, const char* key, int val); + + void GenMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold); + void FindPathMoving(const GameField& field, std::vector & movs, Gem cur, int x, int y, bool hold); + MovingSimple AISearch(AI_Param ai_param, const GameField& pool, int hold, Gem cur, int x, int y, const std::vector& next, bool canhold, int upcomeAtt, int maxDeep, int & searchDeep, int level); + int RunAI(Moving& ret_mov, int& flag, const AI_Param& ai_param, const GameField& pool, int hold, Gem cur, int x, int y, const std::vector& next, bool canhold, int upcomeAtt, int maxDeep, int & searchDeep, int level, int player); + int RunAIDll(TetrisAI_t func, Moving& ret_mov, int& flag, const AI_Param& ai_param, const GameField& pool, int hold, Gem cur, int x, int y, const std::vector& next, bool canhold, int upcomeAtt, int maxDeep, int & searchDeep, int level, int player); +} diff --git a/tetris_ai/tetris_ai.rc b/tetris_ai/tetris_ai.rc new file mode 100644 index 0000000..2f79488 Binary files /dev/null and b/tetris_ai/tetris_ai.rc differ diff --git a/tetris_ai/tetris_ai.vcxproj b/tetris_ai/tetris_ai.vcxproj new file mode 100644 index 0000000..d01c77d --- /dev/null +++ b/tetris_ai/tetris_ai.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + XP_Release + Win32 + + + + {1063E110-F488-4ADA-8632-AB6E53E8BEDD} + Win32Proj + tetris_ai + Tspin + + + + Application + true + v110 + Unicode + + + Application + false + v110 + true + Unicode + + + Application + false + v110_xp + true + Unicode + + + + + + + + + + + + + + + + true + + + false + + + false + false + $(SolutionDir)bin\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + + + Console + true + + + + + Level3 + + + Disabled + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + + Console + true + true + true + false + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;XP_RELEASE;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tetris_ai/tetris_ai.vcxproj.filters b/tetris_ai/tetris_ai.vcxproj.filters new file mode 100644 index 0000000..5445c3a --- /dev/null +++ b/tetris_ai/tetris_ai.vcxproj.filters @@ -0,0 +1,91 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {40211445-1d4c-4629-b0b9-18328834d457} + + + {0c137e9e-d41e-4606-b80a-1bb4e4dd5d6c} + + + {65aa26b2-db4a-4b7d-bb1d-869316f81f01} + + + + + Source Files + + + ai + + + Source Files + + + ai + + + Source Files + + + ai + + + + + ai + + + ai + + + util + + + ai + + + ai + + + Header Files + + + tetris + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/tetris_ai/tetris_gem.cpp b/tetris_ai/tetris_gem.cpp new file mode 100644 index 0000000..648fca3 --- /dev/null +++ b/tetris_ai/tetris_gem.cpp @@ -0,0 +1,129 @@ +#include "tetris_gem.h" + +namespace AI { + Gem gems[8][4]; + int GEM_COL_H[8][4][4]; + int GEM_MAXH[8][4]; + + class init_obj { + public: + init_obj() { + Gem _gems[8][4] = { + { + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + }, + /* //QQTetris + { // I + { 0,15, 0, 0}, + { 4, 4, 4, 4}, + { 0,15, 0, 0}, + { 4, 4, 4, 4}, + }, + { // T + { 0, 7, 2, 0}, + { 2, 6, 2, 0}, + { 2, 7, 0, 0}, + { 2, 3, 2, 0}, + }, + { // L + { 3, 2, 2, 0}, + { 0, 7, 1, 0}, + { 2, 2, 6, 0}, + { 4, 7, 0, 0}, + }, + { // J + { 6, 2, 2, 0}, + { 1, 7, 0, 0}, + { 2, 2, 3, 0}, + { 0, 7, 4, 0}, + }, + { // O + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + }, + { // Z + { 3, 6, 0, 0}, + { 4, 6, 2, 0}, + { 3, 6, 0, 0}, + { 4, 6, 2, 0}, + }, + { // S + { 6, 3, 0, 0}, + { 2, 6, 4, 0}, + { 6, 3, 0, 0}, + { 2, 6, 4, 0}, + }, + */ + { // I 1 + { 0,15, 0, 0}, + { 2, 2, 2, 2}, + { 0, 0,15, 0}, + { 4, 4, 4, 4}, + }, + { // T 2 + { 2, 7, 0, 0}, + { 2, 3, 2, 0}, + { 0, 7, 2, 0}, + { 2, 6, 2, 0}, + }, + { // L 3 + { 4, 7, 0, 0}, + { 3, 2, 2, 0}, + { 0, 7, 1, 0}, + { 2, 2, 6, 0}, + }, + { // J 4 + { 1, 7, 0, 0}, + { 2, 2, 3, 0}, + { 0, 7, 4, 0}, + { 6, 2, 2, 0}, + }, + { // Z 5 + { 3, 6, 0, 0}, + { 2, 3, 1, 0}, + { 0, 3, 6, 0}, + { 4, 6, 2, 0}, + }, + { // S 6 + { 6, 3, 0, 0}, + { 1, 3, 2, 0}, + { 0, 6, 3, 0}, + { 2, 6, 4, 0}, + }, + { // O 7 + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + { 6, 6, 0, 0}, + }, + }; + //int m[] = {1, 2, 4, 4, 4, 1, 2, 2}; // QQTetris + int m[] = {1, 4, 4, 4, 4, 4, 4, 1}; // TOP SRS + for ( int n = 0; n < 8; ++n) { + for ( int s = 0; s < 4; ++s) { + gems[n][s] = _gems[n][s]; + gems[n][s].num = n; + gems[n][s].mod = m[n]; + gems[n][s].spin = s % m[n]; + //GEM_COL_H Initialization + GEM_MAXH[n][s] = 0; + for ( int x = 0; x < 4; ++x) { + GEM_COL_H[n][s][x] = 0; + for ( int y = 0; y < 4; ++y) { + if(gems[n][s].bitmap[y] & (1 << x)) { + GEM_COL_H[n][s][x] = y + 1; + if ( GEM_MAXH[n][s] < y ) GEM_MAXH[n][s] = y; + //I vertical: 0 4 0 0 + } + } + } + } + } + } + } _o; +} diff --git a/tetris_ai/tetris_gem.h b/tetris_ai/tetris_gem.h new file mode 100644 index 0000000..3078704 --- /dev/null +++ b/tetris_ai/tetris_gem.h @@ -0,0 +1,32 @@ +#pragma once +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + +namespace AI { + enum GemType { + GEMTYPE_NULL, + GEMTYPE_I, + GEMTYPE_T, + GEMTYPE_L, + GEMTYPE_J, + GEMTYPE_Z, + GEMTYPE_S, + GEMTYPE_O, + }; + struct Gem { + unsigned long bitmap[4]; + int num, spin, mod; + int geth() const { return 4; } + }; + inline Gem& getGem( int number, int spin ) { + extern Gem gems[8][4]; + return gems[number][spin]; + } + inline int getGemColH( int number, int spin, int x) { + extern int GEM_COL_H[8][4][4]; + return GEM_COL_H[number][spin][x]; + } + inline int getGemMaxH( int number, int spin) { + extern int GEM_MAXH[8][4]; + return GEM_MAXH[number][spin]; + } +} diff --git a/tetris_ai/tetris_setting.h b/tetris_ai/tetris_setting.h new file mode 100644 index 0000000..2359b20 --- /dev/null +++ b/tetris_ai/tetris_setting.h @@ -0,0 +1,26 @@ +#ifdef XP_RELEASE +#define PUBLIC_VERSION 1 // ·¢²¼Ä£Ê½ +#define GAMEMODE_4W 0 // 4wģʽ +#define PLAYER_WAIT 0 // Íæ¼ÒµÈ´ýAI˼¿¼£¬Èç¹ûÐèÒªµÄ»° +#define AI_TRAINING_SLOW 1 +#define USE4W 1 +#else +#define PUBLIC_VERSION 0 +#define GAMEMODE_4W 0 +#define PLAYER_WAIT 0 +#define AI_TRAINING_SLOW 1 // ѵÁ·Ä£Ê½ÂýËÙÑÝʾ +#define USE4W 0 +#endif +#define ATTACK_MODE 1 // À¬»øÐУº0¿ÕÆø 1TOP 2»ðÆ´ +#define AI_SHOW 0 // ²»Ï໥¹¥»÷£¬Î§¹ÛAI +#define DISPLAY_NEXT_NUM 6 // ÏÔʾnext¸öÊý +#if AI_TRAINING_SLOW +#define AI_TRAINING_DEEP 16 +#else +#define AI_TRAINING_DEEP 6 // ѵÁ·AI˼¿¼Éî¶È +#endif +#define TRAINING_ROUND 20 +#define AI_TRAINING_0 9 +#define AI_TRAINING_2 6 + +#define AI_DLL_VERSION 2 // dll°æ±¾ diff --git a/tetris_ai/tetrisgame.h b/tetris_ai/tetrisgame.h new file mode 100644 index 0000000..ce979b3 --- /dev/null +++ b/tetris_ai/tetrisgame.h @@ -0,0 +1,335 @@ +#pragma once + +#include +#include +#include +#include + +#include "tetris.h" +#include "tetris_ai.h" +#include "random.h" + +#include "sound.h" + +#include "tetris_setting.h" + +class TetrisGame : public AI::Tetris { +public: + TetrisGame() { + m_base = AI::point(0, 30); + m_size = AI::point(20, 20); + hold = true; + ai_movs_flag = -1; + reset((unsigned)time(0)); + ai_delay = 0; + mov_llrr = 0; + env_change = 0; + n_win = 0; + last_max_combo = 0; + mSFXon = false; + m_lr = 0; + pAIName = NULL; + pTetrisAI = NULL; + AI::AI_Param param = { + +// 18, 37, 11, 12, 9, 2, -30, -36, -14, 0, 8, -2, 9, +// 21, 30, -4, 9, 0, 5, -22, -6, -12, 9, 4, -3, 14 +// 16, 29, 7, 7, 50, 6, -23, -24, -4, 9, 6, 60, 13 +// 16, 29, 7, 7, 50, 6, -23, -24, -4, 9, 6, 60, 7 +// 21, 21, 19, 12, 10, 7, -17, -2, -3, 6, 9, 15, 15 +// 19, 32, 14, 13, 3, 22, -18, -15, -14, 11, 11, 5, 33 +// 21, 30, 15, 13, 5, -2, -15, -20, -14, 9, 7, 4, 25 +// 21, 30, 15, 13, 5, -2, -15, -20, -14, 9, 7, 30, 25 +// 17, 34, 28, 20, 57, 3, -10, 29, -72, 17, 10, 50, 27 + +// 9, 14, 7, 9, 9, 11, 11, 5, 13, 2, 8, 10, 13 +// 8, 13, 11, 7, 7, 12, 6, 2, 12, 3, 10, 12, 18 +// 8, 13, 11, 7, 7, 12, 6, 2, 12, 3, 10, 12, 18, 18 +// 7, 29, 16, 13, 11, 19, 7, 7, 20, 6, 14, 13, 13, 13 +// 12, 14, 8, 15, 8, 14, 8, 4, 19, 2, 14, 10, 11, 11 +// 21, 27, 15, 6, 40, 8, 20, 6, 7, -6, 3, 16, 8, 8 +// 19, 27, 14, 5, 17, 12, 19, 5, 11, -2, 3, 13, 10, 14 +// 13, 53, 45, 17, 15, 11, 12, 5, 7, 4, 8, 17, 18, 3 +// 17, 26, 18, 1, 9, 12, 16, 4, 16, -4, 11, 7, 11, 14 +// 19, 25, 20, 6, 23, 13, 12, 3, 14, 5, 13, 10, 13, 26 +// 11, 27, 21, 3, 28, 10, 17, 5, 20, -2, 11, 15, 31, 11 +// 8, 26, 16, 5, 23, 12, 7, 5, 8, -8, 14, 15, 9, 18 // APL ¸ß +// 8, 26, 21, 7, 21, 12, 5, 5, 1, -3, 13, 11, 11, 4 +// 13, 28, 21, 14, 25, 11, 21, 6, 8, -3, 13, 24, 31, 1 +// 8, 26, 21, 7, 21, 12, 5, 5, 1, -3, 13, 11, 11, 4 +// 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, +// 8, 22, 12, 9, 11, 6, 7, 5, 15, 2, 8, 7, 10, 16 +// 12, 29, 22, 13, 28, 11, 17, 7, 20, -1, 15, 13, 26, 17 +// 9, 28, 24, 10, 13, 12, 3, 3, 14, -3, 11, 28, 26, 9 +// 8, 26, 21, 7, 21, 12, 5, 5, 20, -3, 13, 21, 11, 4 +// 8, 38, 18, 13, -14, 19, 7, 4, 14, -15, 9, 19, 16, -3 +// 5, 38, 20, 5, 8, 39, 7, 8, -13, -5, 4, 24, 7, -20 +// 36, 34, 50, 8, 12, 12, 7, 10, 2, 6, 11, 10, 9, 6 // ÍÚÍÚ»ú +// 22, 36, 56, 17, 17, 29, 6, 6, 6, 7, 18, 13, 8, 21 +// 3, 34, 38, 12, 20, 16, 10, 7, 10, 10, 14, 35, 12, 0 +// 26, 35, 45, 14, -2, 10, 6, 7, 4, 8, 13, 14, 8, 1 +// 27, 43, 52, 9, 53, 8, 13, 11, 3, 0, 18, 11, 17, 20 +// 32, 43, 60, 9, 8, 24, 13, 8, 2, 7, 20, 10, 13, 21 +// 28, 41, 63, 8, 30, 9, 12, 10, -5, 6, 15, 9, 20, 21 +// 43, 43, 64, 21, 43, 12, 10, 16, 20, 22, 0, 17, 57, 12 +// 28, 41, 69, 28, 0, 16, 34, 12, 4, 21, 3, 0, 20, 14, 50 +// 28, 41, 69, 28, 0, 16, 34, 12, 4, 21, 3, 12, 20, 14, 50, 1000 +// 40, 32, 76, 16, 41, 17, 13, 21, 10, 21, 3, 21, 70, 17, 54 +// 28, 41, 69, 28, 51, 16, 34, 12, 4, 21, 3, 12, 20, 14, 50, 0 +// 47, 26, 70, 4, 46, 16, 26, 21, 7, 31, 14, 17, 69, 1, 43, 300 +// 34, 18, 61, 9, 39, 13, 22, 13, -9, 38, 9, 15, 64, 9, 36, 300 + +// 47, 26, 70, 4, 46, 16, 26, 21, 7, 31, 14, 17, 69, 11, 53, 300 + +// 33, 25, 57, 19, 37, 11, 33, 10, 9, 38, 12, 13, 63, 11, 51, 300 +// 37, 24, 50, 29, 53, 15, 26, 12, 13, 36, 11, 7, 69, 12, 53, 300 +// 36, 25, 50, 20, 55, 15, 28, 12, 15, 36, 12, 10, 70, 12, 53, 300 +// 40, 15, 50, 20, 56, 16, 17, 12, 7, 55, 99, 23, 78, 16, 67, 300 // 1.4.1 +// 21, 20, 66, 40, 27, 22, 48, 26, 8, 71, 13, 24, 92, 7, 69, 300 +// 49, 18, 76, 33, 39, 27, 32, 25, 22, 99, 41, 29, 96, 14, 60, 300 //1.4.3 + +// 40, 27, 65, 22, 37, 18, 66, 28, 9, 32, 4, 16, 93, 10, 27, 300, + +// 45, 28, 84, 21, 35, 27, 56, 30, 9, 64, 13, 18, 97, 11, 29, 300 //1.4.4 +// 40, 20, 98, 13, 35, 22, 63, 29, 5, 68, 11, 15, 98, 14, 32, 300 +// 44, 32, 96, 28, 42, 24, 49, 25, -6, 58, 17, 20, 51, 10, 32, 300 +// 36, 20, 99, 27, 41, 32, 28, 24, 11, 56, 26, 24, 43, 14, 27, 300 //1.4.4 +// 44, 13, 98, 31, 51, 30, 53, 27, 16, 56, 29, 27, 34, 16, 24, 300 + +// 36, 30, 71, 67, 72, 48, 22, 16, 34, 60, 0, 34, 46, 35, 16, 33 // test +// 61, 33, 87, 52, 85, 30, 30, 28, 2, 71, 55, 51, 98, 35, 57, 60 // test +//70, 16, 99, 50, 95, 33, 21, 29, 38, 98, 10, 54, 91, 26, 42, 65 +//45, 28, 84, 11, 35, 27, 26, 30, 39, 64, 13, 18, 97, 11, 29, 300 //1.4.4 +//13, 9, 17, 10, 29, 25, 39, 2, 12, 19, 7, 24, 21, 16, 14, 19, 20 + 26, 0, 20, 17, 45, 41, -8, 9, 10, 27, 15, 20, 19, 11, 44, 4, 300//,1183 + + }; + if ( GAMEMODE_4W ) { + //param.h_variance = 0; + param.tspin = 0; + param.tspin3 = 0; + } + m_ai_param = param; + } + void reset ( unsigned seed, unsigned pass = 0 ) { + while ( ai_movs_flag != -1 ) ::Sleep(1); + last_max_combo = m_max_combo; + AI::Tetris::reset(seed, 10, 22); + m_randatt.seed( seed ); + for ( unsigned i = 0; i < pass; ++i ) m_randatt.rand(); + ai_delay = 0; + mov_llrr = 0; + env_change = 0; + ai_movs.movs.clear(); + n_pieces = 0; + ai_last_deep = 0; + ai_movs_flag = -1; + accept_atts.clear(); + m_last_hole_x = -1; + total_clears = 0; + total_atts = 0; + total_sent = 0; + total_accept_atts = 0; + m_ko = 0; + m_att_info = ""; + } + bool tryXMove(int dx) { + bool ret = Tetris::tryXMove( dx ); + if ( mSFXon && ret ) { + GameSound::ins().mSFX_move.play( m_lr ); + } + return ret; + } + bool tryXXMove(int dx) { + bool ret = Tetris::tryXMove( dx ); + while ( Tetris::tryXMove(dx) ) ; + if ( mSFXon && ret ) { + GameSound::ins().mSFX_move.play( m_lr ); + } + return ret; + } + bool tryYMove(int dy) { + bool ret = Tetris::tryYMove( dy ); + if ( mSFXon && ret ) { + GameSound::ins().mSFX_softdrop.play( m_lr ); + } + return ret; + } + bool tryYYMove(int dy) { + bool ret = Tetris::tryYMove( dy ); + while ( Tetris::tryYMove(dy) ) ; + if ( mSFXon && ret ) { + GameSound::ins().mSFX_softdrop.play( m_lr ); + } + return ret; + } + bool trySpin(int dSpin) { + bool ret = Tetris::trySpin( dSpin ); + if ( mSFXon && ret ) { + GameSound::ins().mSFX_rotate.play( m_lr ); + } + return ret; + } + bool trySpin180() { + bool ret = Tetris::trySpin180( ); + if ( mSFXon && ret ) { + GameSound::ins().mSFX_rotate.play( m_lr ); + } + return ret; + } + bool tryHold() { + bool ret = Tetris::tryHold( ); + if ( mSFXon && ret ) { + GameSound::ins().mSFX_hold.play( m_lr ); + } + return ret; + } + bool drop() { + bool ret = Tetris::drop( ); + if ( mSFXon && ret ) { + GameSound::ins().mSFX_lockdown.play( m_lr ); + } + return ret; + } + void clearSFX( ) { + if ( m_clear_info.clears <= 0 || ! mSFXon ) return ; + int att = m_clear_info.attack; + int b2b = m_clear_info.b2b > 1; + int combo = m_clear_info.combo; + att -= b2b; + att -= AI::getComboAttack( combo ); + if ( m_clear_info.pc ) { + att -= 6; + if ( GameSound::ins().mSFX_pc.isOpen() ) + GameSound::ins().mSFX_pc.play( m_lr ); + } + if ( combo > 1 ) { + int i = 0; + for ( ; i < combo - 1 && i < 20; ++i) { + if ( ! GameSound::ins().mSFX_combo[i].isOpen() ) break; + } + if ( --i >= 0 ) { + if ( GameSound::ins().mSFX_combo[i].isOpen() ) GameSound::ins().mSFX_combo[i].play( m_lr ); + } + } + if ( m_clear_info.wallkick_spin && att > 0 ) { + if ( m_clear_info.wallkick_spin == 2 && (AI::isEnableAllSpin() || m_clear_info.clears == 1) ) { + if ( b2b ) { + GameSound::sound& m = GameSound::ins().mSFX_b2b_tspin[0]; + if (m.isOpen()) m.play( m_lr ); + } else { + GameSound::sound& m = GameSound::ins().mSFX_tspin[0]; + if (m.isOpen()) m.play( m_lr ); + } + } else { + if ( b2b ) { + GameSound::sound& m = GameSound::ins().mSFX_b2b_tspin[m_clear_info.clears]; + if (m.isOpen()) m.play( m_lr ); + } else { + GameSound::sound& m = GameSound::ins().mSFX_tspin[m_clear_info.clears]; + if (m.isOpen()) m.play( m_lr ); + } + } + } else if ( m_clear_info.clears > 0 ) { + if ( b2b ) { + GameSound::sound& m = GameSound::ins().mSFX_b2b_tetris; + if (m.isOpen()) m.play( m_lr ); + } else { + GameSound::sound& m = GameSound::ins().mSFX_clears[m_clear_info.clears - 1]; + if (m.isOpen()) m.play( m_lr ); + } + } + } + bool ko () { + if ( mSFXon ) { + if ( m_ko ) { + if ( GameSound::ins().mSFX_ko.isOpen() ) { + GameSound::ins().mSFX_ko.play(); + return true; + } + } else { + if ( GameSound::ins().mSFX_gameover.isOpen() ) { + GameSound::ins().mSFX_gameover.play(); + return true; + } + } + } + return false; + } + void acceptAttackSFX() { + if ( mSFXon && GameSound::ins().mSFX_lineattack.isOpen() ) { + GameSound::ins().mSFX_lineattack.play( m_lr ); + } + } + void acceptAttack(int n) { + int att[2] = {0}; + for ( int i = 0; i < 32; i += 2) { + att[0] |= 1 << i; + } + att[0] &= m_pool.m_w_mask; + att[1] = ~att[0] & m_pool.m_w_mask; + + int rowdata = m_randatt.randint( m_pool.m_w ); + while ( m_last_hole_x == rowdata ) { + rowdata = m_randatt.randint( m_pool.m_w ); + } + m_last_hole_x = rowdata; + rowdata = ~( 1 << rowdata ) & m_pool.m_w_mask; + for ( ; n > 0; --n ) { + if ( ATTACK_MODE == 0 ) addRow( 0 ); // ¿ÕÆøÐÐ + if ( ATTACK_MODE == 1 ) addRow( rowdata ); // TOPÐÐ + if ( ATTACK_MODE == 2 ) addRow( att[n&1] ); // »ðÆ´ÐÐ + ++total_accept_atts; + } + if ( alive() ) { + if ( m_pool.isCollide(m_cur_x, m_cur_y, m_cur) ) { + m_state = STATE_OVER; + m_ko = 1; + } + } + } + bool game() { + bool ret = AI::Tetris::game(); + if ( ret ) m_piecedelay = 0; + else ++m_piecedelay; + if ( GAMEMODE_4W && AI_SHOW ) { + if ( m_clearLines > 0 ) { + for ( int i = 1; i <= m_clearLines; ++i) { + setRow( poolh() - 40 + i, + ~(15 << 3) & m_pool.m_w_mask ); + } + } + } + return ret; + } + void soundon( bool on ) { + mSFXon = on; + } +public: + bool hold; + AI::Moving ai_movs; + int ai_movs_flag; + int ai_last_deep; + int ai_delay; + AI::AIName_t pAIName; + AI::TetrisAI_t pTetrisAI; + int mov_llrr; + int env_change; + int n_pieces; + std::vector accept_atts; + int m_last_hole_x; + int n_win; + int total_clears; + int total_atts; + int total_sent; + int total_accept_atts; + int last_max_combo; + int m_ko; + int m_lr; // 3dÒôЧ¿ªÆô + int m_piecedelay; + AI::Random m_randatt; + AI::AI_Param m_ai_param; + std::string m_name; + bool mSFXon; + mutable std::string m_att_info; +};