diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + 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. + + + Copyright (C) + + 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: + + Copyright (C) + 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 +. diff --git a/README.md b/README.md index 439c057ab..cb3711be1 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ An example for the Battleship puzzle demonstrates the proper format for XML file ``` - + diff --git a/src/main/java/edu/rpi/legup/app/Config.java b/src/main/java/edu/rpi/legup/app/Config.java index c5e51fb47..b03b01cb9 100644 --- a/src/main/java/edu/rpi/legup/app/Config.java +++ b/src/main/java/edu/rpi/legup/app/Config.java @@ -19,6 +19,7 @@ public class Config { private final static Logger Logger = LogManager.getLogger(Config.class.getName()); private Map puzzles; + private Map fileCreationDisabledStatuses; private static final String CONFIG_LOCATION = "edu/rpi/legup/legup/config"; /** @@ -27,7 +28,8 @@ public class Config { * @throws InvalidConfigException */ public Config() throws InvalidConfigException { - puzzles = new Hashtable<>(); + this.puzzles = new Hashtable<>(); + this.fileCreationDisabledStatuses = new Hashtable<>(); loadConfig(this.getClass().getClassLoader().getResourceAsStream(CONFIG_LOCATION)); } @@ -40,6 +42,16 @@ public List getPuzzleNames() { return new ArrayList<>(puzzles.keySet()); } + public List getFileCreationEnabledPuzzles() + { + LinkedList puzzles = new LinkedList(); + for (String puzzle : this.puzzles.keySet()) + if (!this.fileCreationDisabledStatuses.get(puzzle)) + puzzles.add(puzzle); + return puzzles; + } + + /** * Gets a {@link edu.rpi.legup.model.Puzzle} class for a puzzle name * @@ -77,8 +89,10 @@ private void loadConfig(InputStream stream) throws InvalidConfigException { Element puzzle = (Element) puzzleNodes.item(i); String name = puzzle.getAttribute("name"); String className = puzzle.getAttribute("qualifiedClassName"); + boolean status = Boolean.parseBoolean(puzzle.getAttribute("fileCreationDisabled").toLowerCase()); Logger.debug("Class Name: "+className); - puzzles.put(name, className); + this.puzzles.put(name, className); + this.fileCreationDisabledStatuses.put(name, Boolean.valueOf(status)); } } catch (ParserConfigurationException | SAXException | IOException e) { diff --git a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java index 7016d2421..86422ca16 100644 --- a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java +++ b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java @@ -6,6 +6,8 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.ui.ProofEditorPanel; +import edu.rpi.legup.ui.PuzzleEditorPanel; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -42,6 +44,10 @@ public class GameBoardFacade implements IHistorySubject { private LegupUI legupUI; + private ProofEditorPanel puzzleSolver; + + private PuzzleEditorPanel puzzleEditor; + private String curFileName; private History history; @@ -73,13 +79,16 @@ public synchronized static GameBoardFacade getInstance() { public void initializeUI() { EventQueue.invokeLater(() ->{ legupUI = new LegupUI(); - addHistoryListener(legupUI); + puzzleSolver = legupUI.getProofEditor(); + puzzleEditor = legupUI.getPuzzleEditor(); + addHistoryListener(legupUI.getProofEditor()); + addHistoryListener(legupUI.getPuzzleEditor()); }); } public void setPuzzle(Puzzle puzzle) { this.puzzle = puzzle; - this.legupUI.setPuzzleView(puzzle); + this.puzzleSolver.setPuzzleView(puzzle); this.history.clear(); } @@ -93,10 +102,57 @@ public static void setupConfig() { GameBoardFacade.getInstance().setConfig(config); } + + public void setPuzzleEditor(Puzzle puzzle) { + this.puzzle = puzzle; + this.puzzleEditor.setPuzzleView(puzzle); + } + public void setConfig(Config config) { this.config = config; } + /** + * Loads an empty puzzle + * + * @param game name of the puzzle + * @param rows the number of rows on the board + * @param columns the number of columns on the board + */ + public void loadPuzzle(String game, int rows, int columns) throws RuntimeException { + String qualifiedClassName = config.getPuzzleClassForName(game); + LOGGER.debug("Loading " + qualifiedClassName); + + try { + Class c = Class.forName(qualifiedClassName); + Constructor cons = c.getConstructor(); + Puzzle puzzle = (Puzzle) cons.newInstance(); + setWindowTitle(puzzle.getName(), "New " + puzzle.getName() + " Puzzle"); + + PuzzleImporter importer = puzzle.getImporter(); + if (importer == null) { + LOGGER.error("Puzzle importer is null"); + throw new RuntimeException("Puzzle importer null"); + } + + importer.initializePuzzle(rows, columns); + + puzzle.initializeView(); +// puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); + setPuzzleEditor(puzzle); + } + catch (IllegalArgumentException exception) + { + throw new IllegalArgumentException(exception.getMessage()); + } + catch(ClassNotFoundException | NoSuchMethodException | InvocationTargetException | + IllegalAccessException | InstantiationException e) + { + LOGGER.error(e); + throw new RuntimeException("Puzzle creation error"); + } + } + /** * Loads a puzzle file * diff --git a/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java b/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java index 6711e3035..e1a769610 100644 --- a/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java +++ b/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java @@ -7,8 +7,8 @@ import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.rules.RuleType; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import javax.swing.*; import java.awt.event.KeyEvent; @@ -16,9 +16,6 @@ import java.util.HashMap; import java.util.Map; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import static edu.rpi.legup.app.GameBoardFacade.getInstance; public class PuzzleKeyAccelerator implements KeyListener { diff --git a/src/main/java/edu/rpi/legup/controller/Controller.java b/src/main/java/edu/rpi/legup/controller/Controller.java index 97a0bef92..e52b9133f 100644 --- a/src/main/java/edu/rpi/legup/controller/Controller.java +++ b/src/main/java/edu/rpi/legup/controller/Controller.java @@ -117,7 +117,7 @@ public void mouseMoved(MouseEvent e) { */ @Override public void mouseWheelMoved(MouseWheelEvent e) { - System.out.println(e.getWheelRotation()); + // System.out.println(e.getWheelRotation()); if (e.isControlDown()) { if (e.getWheelRotation() != 0){ viewer.zoom(e.getWheelRotation() * 2, e.getPoint()); diff --git a/src/main/java/edu/rpi/legup/controller/EditorElementController.java b/src/main/java/edu/rpi/legup/controller/EditorElementController.java new file mode 100644 index 000000000..52aa67cfe --- /dev/null +++ b/src/main/java/edu/rpi/legup/controller/EditorElementController.java @@ -0,0 +1,43 @@ +package edu.rpi.legup.controller; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.app.LegupPreferences; +import edu.rpi.legup.history.*; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.elements.Element; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.rules.*; +import edu.rpi.legup.model.tree.TreeElement; +import edu.rpi.legup.model.tree.TreeElementType; +import edu.rpi.legup.ui.proofeditorui.rulesview.RuleButton; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.puzzleeditorui.elementsview.ElementButton; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + +public class EditorElementController implements ActionListener { + protected Object lastSource; + + public EditorElementController() { + super(); + } + + public void buttonPressed(Element element) { + // TODO: implement what happens when element is pressed + } + + @Override + public void actionPerformed(ActionEvent e) { + lastSource = e.getSource(); + ElementButton button = (ElementButton) lastSource; + buttonPressed(button.getElement()); + } +} diff --git a/src/main/java/edu/rpi/legup/controller/ElementController.java b/src/main/java/edu/rpi/legup/controller/ElementController.java index a92cf0d2e..80f80db3e 100644 --- a/src/main/java/edu/rpi/legup/controller/ElementController.java +++ b/src/main/java/edu/rpi/legup/controller/ElementController.java @@ -15,7 +15,7 @@ import edu.rpi.legup.ui.boardview.ElementSelection; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.SelectionItemView; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import edu.rpi.legup.history.ICommand; import edu.rpi.legup.history.EditDataCommand; diff --git a/src/main/java/edu/rpi/legup/controller/RuleController.java b/src/main/java/edu/rpi/legup/controller/RuleController.java index 84a32715f..12fef9b77 100644 --- a/src/main/java/edu/rpi/legup/controller/RuleController.java +++ b/src/main/java/edu/rpi/legup/controller/RuleController.java @@ -7,9 +7,9 @@ import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.rules.*; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.rulesview.RuleButton; -import edu.rpi.legup.ui.rulesview.RulePanel; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.rulesview.RuleButton; +import edu.rpi.legup.ui.proofeditorui.rulesview.RulePanel; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; diff --git a/src/main/java/edu/rpi/legup/controller/TreeController.java b/src/main/java/edu/rpi/legup/controller/TreeController.java index 462cbe142..55306798b 100644 --- a/src/main/java/edu/rpi/legup/controller/TreeController.java +++ b/src/main/java/edu/rpi/legup/controller/TreeController.java @@ -3,8 +3,9 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.tree.Tree; import edu.rpi.legup.ui.boardview.BoardView; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; +import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; @@ -149,6 +150,6 @@ public void mouseMoved(MouseEvent e) { */ @Override public void mouseWheelMoved(MouseWheelEvent e) { - super.mouseWheelMoved(e); + super.viewer.scroll(e.getWheelRotation()); } } diff --git a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java index 099af9afa..2f4733a55 100644 --- a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java @@ -2,11 +2,10 @@ import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.TreeElementView; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/ApplyDefaultBasicRuleCommand.java b/src/main/java/edu/rpi/legup/history/ApplyDefaultBasicRuleCommand.java index 7284b8db2..a212ec602 100644 --- a/src/main/java/edu/rpi/legup/history/ApplyDefaultBasicRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ApplyDefaultBasicRuleCommand.java @@ -5,7 +5,7 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.rules.BasicRule; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index 95b644102..c61875b74 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -7,7 +7,7 @@ import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.awt.event.MouseEvent; import java.util.*; diff --git a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java index 9755d9002..4d78564de 100644 --- a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java @@ -4,7 +4,7 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.observer.ITreeListener; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/EditDataCommand.java b/src/main/java/edu/rpi/legup/history/EditDataCommand.java index 67e199934..f4c582220 100644 --- a/src/main/java/edu/rpi/legup/history/EditDataCommand.java +++ b/src/main/java/edu/rpi/legup/history/EditDataCommand.java @@ -7,7 +7,7 @@ import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.awt.event.MouseEvent; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/MergeCommand.java b/src/main/java/edu/rpi/legup/history/MergeCommand.java index 0634906b7..e6ddf6dfb 100644 --- a/src/main/java/edu/rpi/legup/history/MergeCommand.java +++ b/src/main/java/edu/rpi/legup/history/MergeCommand.java @@ -5,7 +5,7 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.rules.MergeRule; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/ValidateBasicRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateBasicRuleCommand.java index d3d1eafb8..55c3421db 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateBasicRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateBasicRuleCommand.java @@ -2,11 +2,10 @@ import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.rules.BasicRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java index 3697c2f2e..38672a555 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java @@ -2,11 +2,10 @@ import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java index 258bb01c6..7033bbc3f 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java @@ -2,10 +2,9 @@ import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/edu/rpi/legup/model/Puzzle.java b/src/main/java/edu/rpi/legup/model/Puzzle.java index 24fa967e1..a94f84b37 100644 --- a/src/main/java/edu/rpi/legup/model/Puzzle.java +++ b/src/main/java/edu/rpi/legup/model/Puzzle.java @@ -1,5 +1,7 @@ package edu.rpi.legup.model; +import edu.rpi.legup.model.elements.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.observer.IBoardListener; @@ -11,6 +13,7 @@ import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.ui.puzzleeditorui.elementsview.NonPlaceableElementPanel; import edu.rpi.legup.utility.LegupUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -53,6 +56,8 @@ public abstract class Puzzle implements IBoardSubject, ITreeSubject { protected List basicRules; protected List contradictionRules; protected List caseRules; + protected List placeableElements; + protected List nonPlaceableElements; /** * Puzzle Constructor - creates a new Puzzle @@ -65,6 +70,9 @@ public Puzzle() { this.contradictionRules = new ArrayList<>(); this.caseRules = new ArrayList<>(); + this.placeableElements = new ArrayList<>(); + this.nonPlaceableElements = new ArrayList<>(); + registerRules(); } @@ -133,6 +141,15 @@ private void registerRules() { */ public abstract Board generatePuzzle(int difficulty); + /** + * Checks if the given height and width are valid board dimensions for the given puzzle + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @return true if the given dimensions are valid for the given puzzle, false otherwise + */ + public abstract boolean isValidDimensions(int rows, int columns); + /** * Determines if the edu.rpi.legup.puzzle was solves correctly * @@ -256,6 +273,13 @@ public String getName() { public List getBasicRules() { return basicRules; } + public List getPlaceableElements() { + return placeableElements; + } + public List getNonPlaceableElements() { + return nonPlaceableElements; + } + /** * Sets the list of basic rules @@ -274,7 +298,12 @@ public void setBasicRules(List basicRules) { public void addBasicRule(BasicRule rule) { basicRules.add(rule); } - + public void addPlaceableElement(PlaceableElement element) { + placeableElements.add(element); + } + public void addNonPlaceableElement(NonPlaceableElement element) { + nonPlaceableElements.add(element); + } /** * Remove a basic rule from this Puzzle * diff --git a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java index 0f73efa22..da15a62d4 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -29,6 +29,20 @@ public PuzzleImporter(Puzzle puzzle) { this.puzzle = puzzle; } + /** + * Initializes an empty puzzle + * + * @param rows number of rows on the puzzle + * @param columns number of columns on the puzzle + * @throws RuntimeException + */ + public void initializePuzzle(int rows, int columns) throws RuntimeException { + if (this.puzzle.isValidDimensions(rows, columns)) + initializeBoard(rows, columns); + else + throw new IllegalArgumentException("Invalid dimensions provided"); + } + /** * Initializes the puzzle attributes * @@ -80,6 +94,15 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { /** * Creates the board for building * + * @param rows number of rows on the puzzle + * @param columns number of columns on the puzzle + * @throws RuntimeException + */ + public abstract void initializeBoard(int rows, int columns); + + /** + * Creates an empty board for building + * * @param node xml document node * @throws InvalidFileFormatException */ diff --git a/src/main/java/edu/rpi/legup/model/elements/Element.java b/src/main/java/edu/rpi/legup/model/elements/Element.java new file mode 100644 index 000000000..0fdabe383 --- /dev/null +++ b/src/main/java/edu/rpi/legup/model/elements/Element.java @@ -0,0 +1,77 @@ +package edu.rpi.legup.model.elements; + +import edu.rpi.legup.model.rules.RuleType; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +public abstract class Element { + protected String elementID; + protected String elementName; + protected String description; + + protected String imageName; + protected ImageIcon image; + + protected ElementType elementType; + + private final String INVALID_USE_MESSAGE; + + public Element(String elementID, String elementName, String description, String imageName) { + this.elementID = elementID; + this.elementName = elementName; + this.description = description; + this.imageName = imageName; + this.INVALID_USE_MESSAGE = "Invalid use of the rule " + this.elementName; + loadImage(); + } + + private void loadImage() { + if (imageName != null) { + this.image = new ImageIcon(ClassLoader.getSystemResource(imageName)); + //Resize images to be 100px wide + Image image = this.image.getImage(); + if(this.image.getIconWidth() < 120) return; + int height = (int) (100 * ((double) this.image.getIconHeight() / this.image.getIconWidth())); + if(height==0){ + System.out.println("height is 0 error"); + System.out.println("height: "+this.image.getIconHeight()); + System.out.println("width: "+this.image.getIconWidth()); + return; + } + BufferedImage bimage = new BufferedImage(100, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bimage.createGraphics(); + g.drawImage(image, 0, 0, 100, height, null); + this.image = new ImageIcon(bimage); + } + } + public String getElementName() { + return elementName; + } + + public void setElementName(String elementName) { + this.elementName = elementName; + } + + public String getElementID() { + return elementID; + } + + public String getDescription() { + return description; + } + + public ImageIcon getImageIcon() { + return image; + } + + public ElementType getElementType() { + return elementType; + } + + public String getInvalidUseOfRuleMessage() + { + return this.INVALID_USE_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/model/elements/ElementType.java b/src/main/java/edu/rpi/legup/model/elements/ElementType.java new file mode 100644 index 000000000..ea47ca0eb --- /dev/null +++ b/src/main/java/edu/rpi/legup/model/elements/ElementType.java @@ -0,0 +1,5 @@ +package edu.rpi.legup.model.elements; + +public enum ElementType { + PLACEABLE, NONPLACEABLE +} diff --git a/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java b/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java new file mode 100644 index 000000000..87859991f --- /dev/null +++ b/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java @@ -0,0 +1,8 @@ +package edu.rpi.legup.model.elements; + +public abstract class NonPlaceableElement extends Element { + public NonPlaceableElement(String elementID, String elementName, String description, String imageName) { + super(elementID, elementName, description, imageName); + this.elementType = ElementType.NONPLACEABLE; + } +} diff --git a/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java new file mode 100644 index 000000000..b22bdbf5e --- /dev/null +++ b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java @@ -0,0 +1,8 @@ +package edu.rpi.legup.model.elements; + +public abstract class PlaceableElement extends Element{ + public PlaceableElement(String elementID, String elementName, String description, String imageName) { + super(elementID, elementName, description, imageName); + this.elementType = ElementType.PLACEABLE; + } +} diff --git a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java index 89c48e25f..d8fa24166 100644 --- a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java +++ b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java @@ -1,7 +1,7 @@ package edu.rpi.legup.model.observer; import edu.rpi.legup.model.tree.TreeElement; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; public interface ITreeListener { /** diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java index 420cc6204..17168ac98 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java @@ -24,7 +24,7 @@ public TreeElement(TreeElementType type) { public abstract boolean isContradictoryBranch(); /** - * Recursively determines if the sub tree rooted at this tree puzzleElement is valid by checking + * Recursively determines if the sub-tree rooted at this tree puzzleElement is valid by checking * whether this tree puzzleElement and all descendants of this tree puzzleElement is justified * and justified correctly * diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipBoard.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipBoard.java deleted file mode 100644 index 154f41fd1..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipBoard.java +++ /dev/null @@ -1,81 +0,0 @@ -package edu.rpi.legup.puzzle.battleship; - -import edu.rpi.legup.model.gameboard.GridBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; - -import java.util.ArrayList; -import java.util.List; - -public class BattleShipBoard extends GridBoard { - - private List east; - private List south; - - /** - * BattleShipBoard Constructor creates a new battleship board from the specified width and height - * - * @param width width of the board - * @param height height of the board - */ - public BattleShipBoard(int width, int height) { - super(width, height); - - this.east = new ArrayList<>(); - this.south = new ArrayList<>(); - - for (int i = 0; i < height; i++) { - east.add(null); - } - for (int i = 0; i < width; i++) { - south.add(null); - } - } - - /** - * BattleShipBoard Constructor creates a new battleship board which is square from the specified size - * - * @param size size of the board - */ - public BattleShipBoard(int size) { - this(size, size); - } - - /** - * Gets the east {@link BattleShipClue} - * - * @return east battle ship clues - */ - public List getEast() { - return east; - } - - /** - * Gets the east {@link BattleShipClue} - * - * @return east battle ship clues - */ - public List getSouth() { - return south; - } - - @Override - public BattleShipCell getCell(int x, int y) { - return (BattleShipCell) super.getCell(x, y); - } - - @Override - public BattleShipBoard copy() { - BattleShipBoard copy = new BattleShipBoard(dimension.width, dimension.height); - for (int x = 0; x < this.dimension.width; x++) { - for (int y = 0; y < this.dimension.height; y++) { - copy.setCell(x, y, getCell(x, y).copy()); - } - } - for(PuzzleElement e : modifiedData) { - copy.getPuzzleElement(e).setModifiable(false); - } - copy.east = this.east; - copy.south = this.south; - return copy; - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellType.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellType.java deleted file mode 100644 index ac6ed1dcc..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellType.java +++ /dev/null @@ -1,21 +0,0 @@ -package edu.rpi.legup.puzzle.battleship; - -public enum BattleShipCellType { - UNKNOWN, WATER, SHIP_SEGMENT_UNKNOWN, SHIP_SIZE_1, SHIP_SEGMENT_TOP, - SHIP_SEGMENT_RIGHT, SHIP_SEGMENT_BOTTOM, SHIP_SEGMENT_LEFT, - SHIP_SEGMENT_MIDDLE, CLUE_NORTH, CLUE_EAST, CLUE_SOUTH, CLUE_WEST; - - public int value; - - BattleShipCellType() { - this.value = this.ordinal(); - } - - public static BattleShipCellType getType(int value) { - BattleShipCellType[] vals = values(); - if (value >= 0 && value < vals.length) { - return vals[value]; - } - return null; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShip.java b/src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java similarity index 72% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShip.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java index b89c448dd..abcfbc3a9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShip.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java @@ -2,22 +2,28 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; -public class BattleShip extends Puzzle { - public BattleShip() { +public class Battleship extends Puzzle { + public Battleship() { super(); - this.name = "BattleShip"; + this.name = "Battleship"; - this.importer = new BattleShipImporter(this); - this.exporter = new BattleShipExporter(this); + this.importer = new BattleshipImporter(this); + this.exporter = new BattleshipExporter(this); - this.factory = new BattleShipCellFactory(); + this.factory = new BattleshipCellFactory(); } + /** + * Initializes the game board. Called by the invoker of the class + */ @Override public void initializeView() { - boardView = new BattleShipView((BattleShipBoard) currentBoard); + boardView = new BattleshipView((BattleshipBoard) currentBoard); + addBoardListener(boardView); } @Override @@ -25,6 +31,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Battleship + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Battleship, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * @@ -33,9 +52,27 @@ public Board generatePuzzle(int difficulty) { */ @Override public boolean isBoardComplete(Board board) { - return false; + BattleshipBoard battleShipBoard = (BattleshipBoard) board; + + for (ContradictionRule rule : contradictionRules) { + if (rule.checkContradiction(battleShipBoard) == null) { + return false; + } + } + for (PuzzleElement data : battleShipBoard.getPuzzleElements()) { + BattleshipCell cell = (BattleshipCell) data; + if (cell.getType() == BattleshipType.UNKNOWN) { + return false; + } + } + return true; } + /** + * Callback for when the board puzzleElement changes + * + * @param board the board that has changed + */ @Override public void onBoardChange(Board board) { @@ -98,7 +135,7 @@ public void importPuzzle(String fileName) { // int y = Integer.valueOf(cells.item(i).getAttributes().getNamedItem("y").getNodeValue()); // String value = cells.item(i).getAttributes().getNamedItem("value").getNodeValue().toUpperCase(); // -// BattleShipCell cell = new BattleShipCell(BattleShipCellType.valueOf(value).ordinal(), new Point(x, y)); +// BattleShipCell cell = new BattleShipCell(BattleShipType.valueOf(value).ordinal(), new Point(x, y)); // battleShipBoard.setCell(x, y, cell); // cell.setModifiable(false); // cell.setGiven(true); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java new file mode 100644 index 000000000..c72aa9ac6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java @@ -0,0 +1,153 @@ +package edu.rpi.legup.puzzle.battleship; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +public class BattleshipBoard extends GridBoard { + + private List east; + private List south; + + /** + * Constructor for creating a rectangular battleship board. + * + * @param width width of the board + * @param height height of the board + */ + public BattleshipBoard(int width, int height) { + super(width, height); + + this.east = new ArrayList<>(); + this.south = new ArrayList<>(); + + for (int i = 0; i < height; i++) { + east.add(null); + } + for (int i = 0; i < width; i++) { + south.add(null); + } + } + + /** + * Constructor for creating a square-sized battleship board. + * + * @param size size of the board + */ + public BattleshipBoard(int size) { + this(size, size); + } + + /** + * Gets the east {@link BattleshipClue} + * + * @return List of BattleShipClue objects on the east + * side of the board + */ + public List getEast() { + return east; + } + + /** + * Gets the east {@link BattleshipClue} + * + * @return east battle ship clues + */ + public List getSouth() { + return south; + } + + @Override + public BattleshipCell getCell(int x, int y) { + return (BattleshipCell) super.getCell(x, y); + } + + @Override + public BattleshipBoard copy() { + BattleshipBoard copy = new BattleshipBoard(dimension.width, dimension.height); + for (int x = 0; x < this.dimension.width; x++) { + for (int y = 0; y < this.dimension.height; y++) { + copy.setCell(x, y, getCell(x, y).copy()); + } + } + for(PuzzleElement e : modifiedData) { + copy.getPuzzleElement(e).setModifiable(false); + } + copy.east = this.east; + copy.south = this.south; + return copy; + } + + /** + * Get a list of all orthogonally adjacent cells. + * + * @param cell The cell to get adjacent cells from. + * @return List of adjacent cells in clockwise order: + * { up, right, down, left } + */ + public List getAdjOrthogonals(BattleshipCell cell) { + List adj = new ArrayList<>(); + Point loc = cell.getLocation(); + BattleshipCell up = getCell(loc.x, loc.y - 1); + BattleshipCell right = getCell(loc.x + 1, loc.y); + BattleshipCell down = getCell(loc.x, loc.y + 1); + BattleshipCell left = getCell(loc.x - 1, loc.y); + adj.add(up); + adj.add(right); + adj.add(down); + adj.add(left); + return adj; + } + + /** + * Get a list of all diagonally adjacent cells. + * + * @param cell The cell to get diagonally adjacent cells from. + * @return List of diagonally adjacent cells in clockwise order: + * { upRight, downRight, downLeft, upLeft } + */ + public List getAdjDiagonals(BattleshipCell cell) { + List dia = new ArrayList<>(); + Point loc = cell.getLocation(); + BattleshipCell upRight = getCell(loc.x + 1, loc.y - 1); + BattleshipCell downRight = getCell(loc.x + 1, loc.y + 1); + BattleshipCell downLeft = getCell(loc.x - 1, loc.y + 1); + BattleshipCell upLeft = getCell(loc.x - 1, loc.y - 1); + dia.add(upRight); + dia.add(downRight); + dia.add(downLeft); + dia.add(upLeft); + return dia; + } + + /** + * Get a list of cells in a row. + * + * @param y The y-coordinate of the row. + * @return List of cells in the row in increasing x-coordinate order. + */ + public List getRow(int y) { + List row = new ArrayList<>(); + for (int x = 0; x < dimension.width; x++) { + row.add(getCell(x, y)); + } + return row; + } + + /** + * Get a list of cells in a column. + * + * @param x The x-coordinate of the column. + * @return List of cells in the column in increasing y-coordinate order. + */ + public List getColumn(int x) { + List column = new ArrayList<>(); + for (int y = 0; y < dimension.height; y++) { + column.add(getCell(x, y)); + } + return column; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCell.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java similarity index 72% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCell.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java index 93607d708..5f2c5b975 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java @@ -4,7 +4,7 @@ import java.awt.*; -public class BattleShipCell extends GridCell { +public class BattleshipCell extends GridCell { /** * BattleShipCell Constructor - creates a BattleShipCell from the specified value and location @@ -12,7 +12,7 @@ public class BattleShipCell extends GridCell { * @param value value of the BattleShipCell * @param location position of the BattleShipCell */ - public BattleShipCell(int value, Point location) { + public BattleshipCell(BattleshipType value, Point location) { super(value, location); } @@ -21,8 +21,8 @@ public BattleShipCell(int value, Point location) { * * @return type of BattleShipCell */ - public BattleShipCellType getType() { - return BattleShipCellType.getType(getData()); + public BattleshipType getType() { + return data; } /** @@ -30,8 +30,8 @@ public BattleShipCellType getType() { * * @return a new copy of the BattleShipCell that is independent of this one */ - public BattleShipCell copy() { - BattleShipCell copy = new BattleShipCell(data, (Point) location.clone()); + public BattleshipCell copy() { + BattleshipCell copy = new BattleshipCell(data, (Point) location.clone()); copy.setIndex(index); copy.setModifiable(isModifiable); copy.setGiven(isGiven); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellController.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java similarity index 57% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellController.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java index b47149b8a..c37fc4184 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java @@ -5,25 +5,25 @@ import java.awt.event.MouseEvent; -public class BattleShipCellController extends ElementController { +public class BattleshipCellController extends ElementController { @Override public void changeCell(MouseEvent e, PuzzleElement data) { - BattleShipCell cell = (BattleShipCell) data; + BattleshipCell cell = (BattleshipCell) data; if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); } else { - if (cell.getData() == BattleShipCellType.SHIP_SEGMENT_MIDDLE.value) { - cell.setData(BattleShipCellType.UNKNOWN.value); + if (cell.getData() == BattleshipType.SHIP_MIDDLE) { + cell.setData(BattleshipType.UNKNOWN); } else { - cell.setData(cell.getData() + 1); + cell.setData(BattleshipType.getType(cell.getData().value + 1)); } } } else if (e.getButton() == MouseEvent.BUTTON3) { - if (cell.getData() == BattleShipCellType.UNKNOWN.value) { - cell.setData(BattleShipCellType.SHIP_SEGMENT_MIDDLE.value); + if (cell.getData() == BattleshipType.UNKNOWN) { + cell.setData(BattleshipType.SHIP_MIDDLE); } else { - cell.setData(cell.getData() - 1); + cell.setData(BattleshipType.getType(cell.getData().value - 1)); } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java similarity index 78% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellFactory.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java index f884ec6b1..7caaad50b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java @@ -10,7 +10,7 @@ import java.awt.*; -public class BattleShipCellFactory extends ElementFactory { +public class BattleshipCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * @@ -20,17 +20,17 @@ public class BattleShipCellFactory extends ElementFactory { * @throws InvalidFileFormatException */ @Override - public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { + public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { try { - BattleShipBoard battleShipBoard = (BattleShipBoard) board; + BattleshipBoard battleShipBoard = (BattleshipBoard) board; int width = battleShipBoard.getWidth(); int height = battleShipBoard.getHeight(); NamedNodeMap attributeList = node.getAttributes(); if (node.getNodeName().equalsIgnoreCase("cell")) { - int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); - int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); - int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); + int value = Integer.parseInt(attributeList.getNamedItem("value").getNodeValue()); + int x = Integer.parseInt(attributeList.getNamedItem("x").getNodeValue()); + int y = Integer.parseInt(attributeList.getNamedItem("y").getNodeValue()); if (x >= width || y >= height) { throw new InvalidFileFormatException("BattleShip Factory: cell location out of bounds"); } @@ -38,7 +38,7 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat throw new InvalidFileFormatException("BattleShip Factory: cell unknown value"); } - BattleShipCell cell = new BattleShipCell(value, new Point(x, y)); + BattleshipCell cell = new BattleshipCell(BattleshipType.getType(value), new Point(x, y)); cell.setIndex(y * height + x); return cell; } else { @@ -61,7 +61,7 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { org.w3c.dom.Element cellElement = document.createElement("cell"); - BattleShipCell cell = (BattleShipCell) puzzleElement; + BattleshipCell cell = (BattleshipCell) puzzleElement; Point loc = cell.getLocation(); cellElement.setAttribute("value", String.valueOf(cell.getData())); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipClue.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClue.java similarity index 77% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipClue.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClue.java index fca600d2d..31652e464 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClue.java @@ -2,11 +2,11 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; -public class BattleShipClue extends PuzzleElement { +public class BattleshipClue extends PuzzleElement { - private BattleShipCellType type; + private BattleshipType type; - public BattleShipClue(int value, int index, BattleShipCellType type) { + public BattleshipClue(int value, int index, BattleshipType type) { super(value); this.index = index; this.type = type; @@ -42,15 +42,15 @@ public Integer getData() { return (Integer) super.getData(); } - public BattleShipCellType getType() { + public BattleshipType getType() { return type; } - public void setType(BattleShipCellType type) { + public void setType(BattleshipType type) { this.type = type; } - public BattleShipClue copy() { + public BattleshipClue copy() { return null; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipClueView.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java similarity index 80% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipClueView.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java index 06e4901d7..f220ba4a2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java @@ -4,12 +4,12 @@ import java.awt.*; -public class BattleShipClueView extends ElementView { +public class BattleshipClueView extends ElementView { private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); private static final Color FONT_COLOR = Color.BLACK; - public BattleShipClueView(BattleShipClue clue) { + public BattleshipClueView(BattleshipClue clue) { super(clue); } @@ -19,8 +19,8 @@ public BattleShipClueView(BattleShipClue clue) { * @return PuzzleElement associated with this view */ @Override - public BattleShipClue getPuzzleElement() { - return (BattleShipClue) super.getPuzzleElement(); + public BattleshipClue getPuzzleElement() { + return (BattleshipClue) super.getPuzzleElement(); } @Override @@ -30,7 +30,7 @@ public void draw(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value; - BattleShipClue clue = getPuzzleElement(); + BattleshipClue clue = getPuzzleElement(); switch (clue.getType()) { case CLUE_NORTH: value = String.valueOf(clue.getData() + 1); @@ -42,7 +42,7 @@ public void draw(Graphics2D graphics2D) { value = String.valueOf(clue.getData()); break; case CLUE_WEST: - value = BattleShipClue.colNumToString(clue.getData() + 1); + value = BattleshipClue.colNumToString(clue.getData() + 1); break; default: value = ""; diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipElementView.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java similarity index 86% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipElementView.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java index a1ba0f9b8..9ed944654 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java @@ -4,7 +4,7 @@ import java.awt.*; -public class BattleShipElementView extends GridElementView { +public class BattleshipElementView extends GridElementView { private static final Stroke OUTLINE_STROKE = new BasicStroke(1); private static final Color OUTLINE_COLOR = new Color(0x212121); @@ -15,14 +15,14 @@ public class BattleShipElementView extends GridElementView { private static final Font FONT = new Font("TimesRoman", Font.BOLD, 10); private static final Color FONT_COLOR = new Color(0xFFEB3B); - public BattleShipElementView(BattleShipCell cell) { + public BattleshipElementView(BattleshipCell cell) { super(cell); } @Override public void drawElement(Graphics2D graphics2D) { - BattleShipCell cell = (BattleShipCell) puzzleElement; - BattleShipCellType type = cell.getType(); + BattleshipCell cell = (BattleshipCell) puzzleElement; + BattleshipType type = cell.getType(); switch (type) { case UNKNOWN: @@ -33,7 +33,7 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.setColor(WATER_COLOR); graphics2D.fillRect(location.x, location.y, size.width, size.height); break; - case SHIP_SEGMENT_UNKNOWN: + case SHIP_UNKNOWN: graphics2D.setColor(SHIP_COLOR); graphics2D.fillRect(location.x + 3 * size.width / 8, location.y + 3 * size.height / 8, size.width / 4, size.height / 4); @@ -46,28 +46,28 @@ public void drawElement(Graphics2D graphics2D) { int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(value, xText, yText); break; - case SHIP_SIZE_1: + case SUBMARINE: graphics2D.setColor(SHIP_COLOR); graphics2D.fillOval(location.x + size.width / 4, location.y + size.width / 4, size.width / 2, size.height / 2); break; - case SHIP_SEGMENT_TOP: + case SHIP_TOP: graphics2D.setColor(SHIP_COLOR); graphics2D.fillArc(location.x, location.y - size.height / 2, size.width, size.height, 180, 180); break; - case SHIP_SEGMENT_RIGHT: + case SHIP_RIGHT: graphics2D.setColor(SHIP_COLOR); graphics2D.fillArc(location.x + size.height / 2, location.y, size.width, size.height, 90, 180); break; - case SHIP_SEGMENT_BOTTOM: + case SHIP_BOTTOM: graphics2D.setColor(SHIP_COLOR); graphics2D.fillArc(location.x, location.y + size.height / 2, size.width, size.height, 0, 180); break; - case SHIP_SEGMENT_LEFT: + case SHIP_LEFT: graphics2D.setColor(SHIP_COLOR); graphics2D.fillArc(location.x - size.height / 2, location.y, size.width, size.height, 270, 180); break; - case SHIP_SEGMENT_MIDDLE: + case SHIP_MIDDLE: graphics2D.setColor(SHIP_COLOR); graphics2D.fillRect(location.x, location.y, size.width, size.height); break; diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipExporter.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java similarity index 79% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipExporter.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java index bb8bd2a12..e8aa00108 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java @@ -4,15 +4,15 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import org.w3c.dom.Document; -public class BattleShipExporter extends PuzzleExporter { +public class BattleshipExporter extends PuzzleExporter { - public BattleShipExporter(BattleShip battleShip) { + public BattleshipExporter(Battleship battleShip) { super(battleShip); } @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - BattleShipBoard board = (BattleShipBoard) puzzle.getTree().getRootNode().getBoard(); + BattleshipBoard board = (BattleshipBoard) puzzle.getTree().getRootNode().getBoard(); org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); @@ -20,8 +20,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); for (PuzzleElement puzzleElement : board.getPuzzleElements()) { - BattleShipCell cell = (BattleShipCell) puzzleElement; - if (cell.getData() != 0) { + BattleshipCell cell = (BattleshipCell) puzzleElement; + if (cell.getData() != BattleshipType.getType(0)) { org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } @@ -30,17 +30,17 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element axisEast = newDocument.createElement("axis"); axisEast.setAttribute("side", "east"); - for (BattleShipClue clue : board.getEast()) { + for (BattleshipClue clue : board.getEast()) { org.w3c.dom.Element clueElement = newDocument.createElement("clue"); clueElement.setAttribute("value", String.valueOf(clue.getData())); - clueElement.setAttribute("index", BattleShipClue.colNumToString(clue.getIndex())); + clueElement.setAttribute("index", BattleshipClue.colNumToString(clue.getIndex())); axisEast.appendChild(clueElement); } boardElement.appendChild(axisEast); org.w3c.dom.Element axisSouth = newDocument.createElement("axis"); axisSouth.setAttribute("side", "south"); - for (BattleShipClue clue : board.getEast()) { + for (BattleshipClue clue : board.getEast()) { org.w3c.dom.Element clueElement = newDocument.createElement("clue"); clueElement.setAttribute("value", String.valueOf(clue.getData())); clueElement.setAttribute("index", String.valueOf(clue.getIndex())); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipImporter.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java similarity index 84% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipImporter.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java index 04a884085..b05e5b1c9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java @@ -8,11 +8,23 @@ import java.awt.*; -public class BattleShipImporter extends PuzzleImporter { - public BattleShipImporter(BattleShip battleShip) { +public class BattleshipImporter extends PuzzleImporter { + public BattleshipImporter(Battleship battleShip) { super(battleShip); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * @@ -32,14 +44,14 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); - BattleShipBoard battleShipBoard = null; + BattleshipBoard battleShipBoard = null; if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); - battleShipBoard = new BattleShipBoard(size); + battleShipBoard = new BattleshipBoard(size); } else if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); - battleShipBoard = new BattleShipBoard(width, height); + battleShipBoard = new BattleshipBoard(width, height); } if (battleShipBoard == null) { @@ -50,9 +62,10 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = battleShipBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - BattleShipCell cell = (BattleShipCell) puzzle.getFactory().importCell(elementDataList.item(i), battleShipBoard); + BattleshipCell cell = (BattleshipCell) puzzle.getFactory().importCell( + elementDataList.item(i), battleShipBoard); Point loc = cell.getLocation(); - if (cell.getData() != 0) { + if (cell.getData() != BattleshipType.getType(0)) { cell.setModifiable(false); cell.setGiven(true); } @@ -62,7 +75,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (battleShipBoard.getCell(x, y) == null) { - BattleShipCell cell = new BattleShipCell(BattleShipCellType.UNKNOWN.value, new Point(x, y)); + BattleshipCell cell = new BattleshipCell(BattleshipType.UNKNOWN, new Point(x, y)); cell.setIndex(y * height + x); cell.setModifiable(true); battleShipBoard.setCell(x, y, cell); @@ -97,7 +110,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int i = 0; i < eastClues.getLength(); i++) { Element clue = (Element) eastClues.item(i); int value = Integer.valueOf(clue.getAttribute("value")); - int index = BattleShipClue.colStringToColNum(clue.getAttribute("index")); + int index = BattleshipClue.colStringToColNum(clue.getAttribute("index")); if (index - 1 < 0 || index - 1 > battleShipBoard.getHeight()) { throw new InvalidFileFormatException("BattleShip Importer: clue index out of bounds"); @@ -106,7 +119,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (battleShipBoard.getEast().get(index - 1) != null) { throw new InvalidFileFormatException("BattleShip Importer: duplicate clue index"); } - battleShipBoard.getEast().set(index - 1, new BattleShipClue(value, index, BattleShipCellType.CLUE_EAST)); + battleShipBoard.getEast().set(index - 1, new BattleshipClue(value, index, BattleshipType.CLUE_EAST)); } for (int i = 0; i < southClues.getLength(); i++) { @@ -121,7 +134,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (battleShipBoard.getSouth().get(index - 1) != null) { throw new InvalidFileFormatException("BattleShip Importer: duplicate clue index"); } - battleShipBoard.getSouth().set(index - 1, new BattleShipClue(value, index, BattleShipCellType.CLUE_SOUTH)); + battleShipBoard.getSouth().set(index - 1, new BattleshipClue(value, index, BattleshipType.CLUE_SOUTH)); } puzzle.setCurrentBoard(battleShipBoard); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java new file mode 100644 index 000000000..46e0304b1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java @@ -0,0 +1,37 @@ +package edu.rpi.legup.puzzle.battleship; + +public enum BattleshipType { + UNKNOWN, WATER, SUBMARINE, SHIP_UNKNOWN, + SHIP_TOP, SHIP_RIGHT, SHIP_BOTTOM, SHIP_LEFT, SHIP_MIDDLE, + CLUE_NORTH, CLUE_EAST, CLUE_SOUTH, CLUE_WEST; + + public int value; + + BattleshipType() { + this.value = this.ordinal(); + } + + /** + * Gets the enum of this BattleShipType + * + * @return enum equivalent BattleShipType of integer value + */ + public static BattleshipType getType(int value) { + BattleshipType[] vals = values(); + if (value >= 0 && value < vals.length) { + return vals[value]; + } + return null; + } + + /** + * Checks if the type is a ship. + * + * @param type the {@link BattleshipType} to check + * @return true if the type is a ship, false otherwise + */ + public static boolean isShip(BattleshipType type) { + return type == SHIP_UNKNOWN || type == SHIP_TOP || type == SHIP_RIGHT + || type == SHIP_BOTTOM || type == SHIP_LEFT || type == SHIP_MIDDLE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipView.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java similarity index 71% rename from src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipView.java rename to src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java index 7c4353888..0768d4f66 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleShipView.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java @@ -6,14 +6,14 @@ import java.awt.*; -public class BattleShipView extends GridBoardView { - public BattleShipView(BattleShipBoard board) { - super(new BoardController(), new BattleShipCellController(), board.getDimension()); +public class BattleshipView extends GridBoardView { + public BattleshipView(BattleshipBoard board) { + super(new BoardController(), new BattleshipCellController(), board.getDimension()); for (PuzzleElement puzzleElement : board.getPuzzleElements()) { - BattleShipCell cell = (BattleShipCell) puzzleElement; + BattleshipCell cell = (BattleshipCell) puzzleElement; Point loc = cell.getLocation(); - BattleShipElementView elementView = new BattleShipElementView(cell); + BattleshipElementView elementView = new BattleshipElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java index c336a4daf..46360c43f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java @@ -3,26 +3,82 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.puzzle.battleship.BattleshipBoard; +import edu.rpi.legup.puzzle.battleship.BattleshipCell; +import edu.rpi.legup.puzzle.battleship.BattleshipType; + +import java.util.List; public class AdjacentShipsContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE + = "No instance of the contradiction " + this.ruleName + " here"; + public AdjacentShipsContradictionRule() { super("BTSP-CONT-0001", "Adjacent Ships", - "", - "edu/rpi/legup/images/battleship/contradictions/AdjacentShips.png"); + "Cells next to the battleship must be water.", + "edu/rpi/legup/images/battleship/contradictions" + + "/AdjacentShips.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific + * {@link PuzzleElement} index using this rule. * * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * @param puzzleElement equivalent {@link PuzzleElement} + * @return null if the transition contains a + * contradiction at the specified {@link PuzzleElement}, + * otherwise return a no contradiction message. */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - return null; + BattleshipBoard bsBoard = (BattleshipBoard) board; + BattleshipCell cell = (BattleshipCell) bsBoard.getPuzzleElement(puzzleElement); + + // rule only applies to battleship cells + if (!BattleshipType.isShip(cell.getType())) + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + + // check orthogonally adjacent cells + List orthoAdjCells + = bsBoard.getAdjOrthogonals(cell); + + BattleshipCell up = orthoAdjCells.get(0); + BattleshipCell right = orthoAdjCells.get(1); + BattleshipCell down = orthoAdjCells.get(2); + BattleshipCell left = orthoAdjCells.get(3); + + boolean isVertical = (up != null && BattleshipType.isShip(up.getData())) + || (down != null && BattleshipType.isShip(down.getData())); + + boolean isHorizontal = (left != null && BattleshipType.isShip(left.getData())) + || (right != null && BattleshipType.isShip(right.getData())); + + // ships cannot be both vertical and horizontal + if (isVertical && isHorizontal) + return null; + + // check diagonally adjacent cells + List diagAdjCells + = bsBoard.getAdjDiagonals(cell); + + BattleshipCell upRight = diagAdjCells.get(0); + BattleshipCell downRight = diagAdjCells.get(1); + BattleshipCell downLeft = diagAdjCells.get(2); + BattleshipCell upLeft = diagAdjCells.get(3); + + // diagonally adjacent cells must be water + if (upRight != null && BattleshipType.isShip(upRight.getData())) + return null; + if (downRight != null && BattleshipType.isShip(downRight.getData())) + return null; + if (downLeft != null && BattleshipType.isShip(downLeft.getData())) + return null; + if (upLeft != null && BattleshipType.isShip(upLeft.getData())) + return null; + + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipBasicRule.java index b30110838..f947fb859 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipBasicRule.java @@ -18,7 +18,7 @@ public ContinueShipBasicRule() { /** * Checks whether the child node logically follows from the parent node * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes + * This method is the one that should be overridden in child classes * * @param transition transition to check * @param puzzleElement equivalent puzzleElement diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java index 8d35d3931..cf6df4064 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java @@ -33,6 +33,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Fillapix + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Fillapix, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + @Override public boolean isBoardComplete(Board board) { FillapixBoard fillapixBoard = (FillapixBoard) board; diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java index 4bf28af2b..0be80324a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java @@ -13,6 +13,18 @@ public FillapixImporter(Fillapix fillapix) { super(fillapix); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java index b28347e0a..951dd5c3e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java @@ -35,6 +35,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for HeyAwake + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for HeyAwake, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java index e72be136a..018246175 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java @@ -14,6 +14,18 @@ public HeyawakeImporter(Heyawake heyawake) { super(heyawake); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java index ed6007c65..0053335e2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java @@ -38,6 +38,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Light Up + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Light Up, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java index 625483090..aa8c8458c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java @@ -13,6 +13,18 @@ public LightUpImporter(LightUp lightUp) { super(lightUp); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java b/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java index 493d5e1ad..8ff001d71 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java @@ -11,7 +11,7 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.treeview.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.awt.event.MouseEvent; diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java b/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java index 7ce783a6a..2134f1f90 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java @@ -36,6 +36,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Masyu + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Masyu, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java index 5e0b4ca57..0f96d59df 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java @@ -13,6 +13,18 @@ public MasyuImporter(Masyu masyu) { super(masyu); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java index 782df59f2..fbd0d3d88 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java @@ -37,6 +37,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Nurikabe + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Nurikabe, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java index c7903a1f1..7667c2c45 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java @@ -6,10 +6,21 @@ public class NurikabeCell extends GridCell { - public NurikabeCell(int valueInt, Point location) { - super(valueInt, location); +/** + * NurikabeCell Constructor - creates a NurikabeCell from the specified value and location + * + * @param value value of the NurikabeCell + * @param location position of the NurikabeCell + */ + public NurikabeCell(int value, Point location) { + super(value, location); } + /** + * Gets the type of this NurikabeCell + * + * @return type of NurikabeCell + */ public NurikabeType getType() { switch (data) { case -2: @@ -26,6 +37,11 @@ public NurikabeType getType() { return null; } + /** + * Performs a deep copy on the NurikabeCell + * + * @return a new copy of the NurikabeCell that is independent of this one + */ @Override public NurikabeCell copy() { NurikabeCell copy = new NurikabeCell(data, (Point) location.clone()); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java index 82efff1c1..f9dfb98f5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java @@ -13,6 +13,18 @@ public NurikabeImporter(Nurikabe nurikabe) { super(nurikabe); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java similarity index 92% rename from src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableBasicRule.java rename to src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java index dd1ef7fae..0cb016a92 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java @@ -10,10 +10,10 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -public class UnreachableBasicRule extends BasicRule { - public UnreachableBasicRule() { +public class CannotReachCellBasicRule extends BasicRule { + public CannotReachCellBasicRule() { super("NURI-BASC-0008", - "Unreachable white region", + "Can't Reach Cell", "A cell must be black if it cannot be reached by any white region", "edu/rpi/legup/images/nurikabe/rules/Unreachable.png"); } @@ -29,7 +29,7 @@ public UnreachableBasicRule() { */ @Override protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - ContradictionRule contraRule = new CantReachWhiteContradictionRule(); + ContradictionRule contraRule = new UnreachableWhiteCellContradictionRule(); NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java index 987d33e9c..5c2abf53a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java @@ -24,12 +24,14 @@ public IsolateBlackContradictionRule() { } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific + * {@link PuzzleElement} index using this rule. * * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * @return null if the transition contains a + * contradiction at the specified {@link PuzzleElement}, + * otherwise return a no contradiction message. */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CantReachWhiteContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java similarity index 92% rename from src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CantReachWhiteContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java index 4118ce41d..f12bdb57f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CantReachWhiteContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java @@ -11,14 +11,14 @@ import java.util.ArrayList; import java.util.Set; -public class CantReachWhiteContradictionRule extends ContradictionRule { +public class UnreachableWhiteCellContradictionRule extends ContradictionRule { private final String NO_CONTRADICTION_MESSAGE = "Cell at this index can be reached"; private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; - public CantReachWhiteContradictionRule() { + public UnreachableWhiteCellContradictionRule() { super("NURI-CONT-0002", - "Unreachables are Black", + "Unreachable White Cell", "A white cell must be able to reach a white region", "edu/rpi/legup/images/nurikabe/contradictions/CantReach.png"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/nurikabe_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/nurikabe_reference_sheet.txt index ae714e83f..2af0b5fea 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/nurikabe_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/nurikabe_reference_sheet.txt @@ -5,11 +5,11 @@ NURI-BASC-0004 : FillinBlackBasicRule NURI-BASC-0005 : FillinWhiteBasicRule NURI-BASC-0006 : PreventBlackSquareBasicRule NURI-BASC-0007 : SurroundRegionBasicRule -NURI-BASC-0008 : UnreachableBasicRule +NURI-BASC-0008 : CannotReachCellBasicRule NURI-BASC-0009 : WhiteBottleNeckBasicRule NURI-CONT-0001 : BlackSquareContradictionRule -NURI-CONT-0002 : CantReachWhiteContradictionRule +NURI-CONT-0002 : UnreachableWhiteCellContradictionRule NURI-CONT-0003 : IsolateBlackContradictionRule NURI-CONT-0004 : MultipleNumbersContradictionRule NURI-CONT-0005 : NoNumberContradictionRule diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java index 62a39e870..e8a23b1e9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java @@ -39,6 +39,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Short Truth Table + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Short Truth Table, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java index cbf052521..a372b9053 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java @@ -214,7 +214,17 @@ private void setGivenCells(ShortTruthTableBoard sttBoard, } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + } //STATEMENT IMPORTER diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java index 6e350c6aa..d45d82c54 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java @@ -2,23 +2,18 @@ import edu.rpi.legup.history.CommandError; import edu.rpi.legup.history.PuzzleCommand; -import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.TreeElementView; -import edu.rpi.legup.ui.treeview.TreeNodeView; -import edu.rpi.legup.ui.treeview.TreeTransitionView; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeTransitionView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class ClueCommand extends PuzzleCommand { private TreeViewSelection selection; private SkyscrapersClueView clueView; diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java index 063804b07..b04fb5123 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java @@ -41,6 +41,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Skyscrapers + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Skyscrapers, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java index 45b8a3f0b..1a5664f94 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java @@ -10,9 +10,9 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.treeview.TreePanel; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.awt.event.MouseEvent; diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java index 1207845a8..727d9957e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java @@ -1,11 +1,8 @@ package edu.rpi.legup.puzzle.skyscrapers; -import edu.rpi.legup.app.LegupPreferences; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; import edu.rpi.legup.ui.boardview.GridElementView; import java.awt.*; -import java.awt.geom.Rectangle2D; public class SkyscrapersElementView extends GridElementView { private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java index 5ddcc673f..0f009a440 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java @@ -13,6 +13,18 @@ public SkyscrapersImporter(Skyscrapers treeTent) { super(treeTent); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java index 2704ce314..f94d2290a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java @@ -46,6 +46,33 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Sudoku + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Sudoku, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // The number of rows and columns must be greater than 1 + if (rows <= 1 || columns <= 1) + return false; + + // The number of rows and columns must be equal + if (rows != columns) + return false; + + // For Sudoku, the number of rows and columns must be a perfect square + // Note: we don't need to check the columns since by this point, we have verified that the number of rows + // equals the number of columns + double sqrtRows = Math.sqrt(rows); + if (sqrtRows - Math.floor(sqrtRows) != 0) + return false; + + return true; + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java index 0805da8f9..f94f516ba 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java @@ -13,6 +13,34 @@ public SudokuImporter(Sudoku sudoku) { super(sudoku); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + SudokuBoard sudokuBoard; + int minorSize = (int) Math.sqrt(rows); + sudokuBoard = new SudokuBoard(rows); + + for (int y = 0; y < columns; y++) { + for (int x = 0; x < rows; x++) { + if (sudokuBoard.getCell(x, y) == null) { + int groupIndex = x / minorSize + y / minorSize * minorSize; + SudokuCell cell = new SudokuCell(0, new Point(x, y), groupIndex, rows); + cell.setIndex(y * rows + x); + cell.setModifiable(true); + sudokuBoard.setCell(x, y, cell); + } + } + } + + puzzle.setCurrentBoard(sudokuBoard); + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java index 4625c6c24..e0e099038 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.sudoku; import edu.rpi.legup.controller.BoardController; -import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.ui.boardview.DataSelectionView; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.GridBoardView; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java b/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java index 0ddf9ac29..5722774ef 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java @@ -4,9 +4,9 @@ import edu.rpi.legup.history.PuzzleCommand; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.treeview.TreeElementView; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java b/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java index 3a42f12da..ea03fc6f5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java @@ -5,9 +5,9 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.treeview.TreeElementView; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.awt.*; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java index 1599965bd..cf5fd5fdf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java @@ -36,6 +36,19 @@ public Board generatePuzzle(int difficulty) { return null; } + @Override + /** + * Determines if the given dimensions are valid for Tree Tent + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Tree Tent, false otherwise + */ + public boolean isValidDimensions(int rows, int columns) { + // This is a placeholder, this method needs to be implemented + throw new UnsupportedOperationException(); + } + /** * Determines if the current board is a valid state * diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java index 776f325b9..73bc9293b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -101,6 +101,14 @@ public void notifyDeletion(PuzzleElement puzzleElement) { } } + /** + * Get a list of all orthogonally adjacent cells. + * + * @param cell The cell to get adjacent cells from. + * @param type The cell types to get. + * @return List of adjacent cells in the form { up, right, down, left }. + * If an adjacent cell is null, it will not be added to the list. + */ public List getAdjacent(TreeTentCell cell, TreeTentType type) { List adj = new ArrayList<>(); Point loc = cell.getLocation(); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java index 833180ee3..1440ced35 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java @@ -10,9 +10,9 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.treeview.TreePanel; -import edu.rpi.legup.ui.treeview.TreeView; -import edu.rpi.legup.ui.treeview.TreeViewSelection; +import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.awt.event.MouseEvent; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java index fdbef245e..68b8fdf70 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java @@ -13,6 +13,18 @@ public TreeTentImporter(TreeTent treeTent) { super(treeTent); } + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException + */ + @Override + public void initializeBoard(int rows, int columns) { + + } + /** * Creates the board for building * diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java index 99d04c2e9..5faa88c59 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java @@ -1,7 +1,8 @@ package edu.rpi.legup.puzzle.treetent; public enum TreeTentType { - UNKNOWN(0), TREE(1), GRASS(2), TENT(3), CLUE_NORTH(-1), CLUE_EAST(-2), CLUE_SOUTH(-3), CLUE_WEST(-4); + UNKNOWN(0), TREE(1), GRASS(2), TENT(3), + CLUE_NORTH(-1), CLUE_EAST(-2), CLUE_SOUTH(-3), CLUE_WEST(-4); public int value; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldBasicRule.java index 9c32f020d..acf85c74b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldBasicRule.java @@ -37,8 +37,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); - if (finalCell.getType() == TreeTentType.GRASS && initCell.getType() == TreeTentType.UNKNOWN) { - return null; + if (!(finalCell.getType() == TreeTentType.GRASS && initCell.getType() == TreeTentType.UNKNOWN)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be grass"; } if (isForced(finalBoard, finalCell)) { diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index cac8aa31a..edf4a657c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -5,8 +5,14 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import edu.rpi.legup.puzzle.treetent.TreeTentClue; import java.util.ArrayList; +import java.util.List; +import java.awt.*; public class FillinRowCaseRule extends CaseRule { @@ -18,7 +24,19 @@ public FillinRowCaseRule() { @Override public CaseBoard getCaseBoard(Board board) { - return null; + TreeTentBoard treeTentBoard = (TreeTentBoard) board.copy(); + treeTentBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(treeTentBoard, this); + ArrayList clues = treeTentBoard.getRowClues(); + clues.addAll(treeTentBoard.getColClues()); + for (PuzzleElement element : clues) { + // if ((((TreeTentCell) element).getType() == TreeTentType.CLUE_SOUTH && treeTentBoard.getRowCol(((TreeTentCell)element).getLocation().y, TreeTentType.UNKNOWN, true).size() != 0) || + // (((TreeTentCell) element).getType() == TreeTentType.CLUE_EAST && treeTentBoard.getRowCol(((TreeTentCell)element).getLocation().x, TreeTentType.UNKNOWN, false).size() != 0)) { + // caseBoard.addPickableElement(element); + // } + caseBoard.addPickableElement(element); + } + return caseBoard; } /** @@ -30,9 +48,92 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList(); + List group; + int tentsLeft; + TreeTentClue clue = ((TreeTentClue) puzzleElement); + int clueIndex = clue.getClueIndex()-1; + TreeTentBoard tBoard = (TreeTentBoard)board; + if(clue.getType() == TreeTentType.CLUE_SOUTH) { + group = tBoard.getRowCol(clueIndex, TreeTentType.UNKNOWN, false); + tentsLeft = tBoard.getRowClues().get(clueIndex).getData() - tBoard.getRowCol(clueIndex, TreeTentType.TENT, false).size(); + cases = genCombinations(tBoard, group, tentsLeft, clueIndex, false); + } else { + group = tBoard.getRowCol(clueIndex, TreeTentType.UNKNOWN, true); + tentsLeft = tBoard.getRowClues().get(clueIndex).getData() - tBoard.getRowCol(clueIndex, TreeTentType.TENT, true).size(); + cases = genCombinations(tBoard, group, tentsLeft, clueIndex, true); + } + + //generate every combination (nCr) + //call goodBoard for each generated combination + //alternitive would be to implement collision avoidance while generating instead of after + if(cases.size() > 0) {return cases;} return null; } + private ArrayList genCombinations(TreeTentBoard iBoard, List tiles, int target, Integer index, boolean isRow) + { + return genCombRecursive(iBoard, tiles, tiles, target, 0, new ArrayList(), index, isRow); + } + + private ArrayList genCombRecursive(TreeTentBoard iBoard, List original, List tiles, int target, int current, List selected, Integer index, boolean isRow) + { + ArrayList b = new ArrayList<>(); + if(target == current) + { + TreeTentBoard temp = iBoard.copy(); + for(TreeTentCell c : original) + { + if(selected.contains(c)) + { + PuzzleElement change = temp.getPuzzleElement(c); + change.setData(TreeTentType.TENT.value); + temp.addModifiedData(change); + } + else + { + PuzzleElement change = temp.getPuzzleElement(c); + change.setData(TreeTentType.GRASS.value); + temp.addModifiedData(change); + } + + } + if(goodBoard(temp, index, isRow)) {b.add(temp);} + return b; + } + for(int i = 0; i < tiles.size(); ++i) + { + List sub = tiles.subList(i+1, tiles.size()); + List next = new ArrayList(selected); + next.add(tiles.get(i)); + b.addAll(genCombRecursive(iBoard, original, sub, target, current+1, next, index, isRow)); + } + return b; + } + + //Effectively runs TouchingTents check on all the added tents to make sure that the proposed board is valid. + //Could check more or less in the future depending on how "smart" this case rule should be. + private boolean goodBoard(TreeTentBoard board, Integer index, boolean isRow) + { + List tents; + if(isRow) + { + tents = board.getRowCol(index, TreeTentType.TENT, true); + } + else + { + tents = board.getRowCol(index, TreeTentType.TENT, false); + } + + for(TreeTentCell t : tents) + { + List adj = board.getAdjacent(t, TreeTentType.TENT); + List diag = board.getDiagonals(t, TreeTentType.TENT); + if(adj.size() > 0 || diag.size() > 0) {return false;} + } + return true; + } + /** * Checks whether the transition logically follows from the parent node using this rule * diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotBasicRule.java index e95f45e9d..836129e17 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotBasicRule.java @@ -43,7 +43,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; } - if (isForced(initialBoard, initCell)) { + if (isForced(finalBoard, finalCell)) { return null; } else { return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; @@ -51,12 +51,25 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } private boolean isForced(TreeTentBoard board, TreeTentCell cell) { - List adjTents = board.getAdjacent(cell, TreeTentType.TREE); - for (TreeTentCell c : adjTents) { - Point loc = c.getLocation(); - for (TreeTentLine line : board.getLines()) { - if (line.getC1().getLocation().equals(loc) || line.getC2().getLocation().equals(loc)) { - return false; + List adjTrees = board.getAdjacent(cell, TreeTentType.TREE); + for (TreeTentCell c : adjTrees) { + List unkAroundTree = board.getAdjacent(c, TreeTentType.UNKNOWN); + List tntAroundTree = board.getAdjacent(c, TreeTentType.TENT); + if(unkAroundTree.size() == 0) + { + if(tntAroundTree.size() == 1) + { + return true; + } + else + { + for(TreeTentCell t : tntAroundTree) + { + if(t == cell) {continue;} + List treesAroundTents = board.getAdjacent(t, TreeTentType.TREE); + if(treesAroundTents.size() == 1) {return false;} + } + return true; } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java index 851e668fd..92b567531 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java @@ -5,6 +5,9 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; import java.util.ArrayList; @@ -18,7 +21,13 @@ public LinkTentCaseRule() { @Override public CaseBoard getCaseBoard(Board board) { - return null; + TreeTentBoard treeTentBoard = (TreeTentBoard) board.copy(); + treeTentBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(treeTentBoard, this); + for(PuzzleElement element : treeTentBoard.getPuzzleElements()) { + if(((TreeTentCell) element).getType() == TreeTentType.TENT) {caseBoard.addPickableElement(element);} + } + return caseBoard; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java index 951989d66..335017c98 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java @@ -5,8 +5,12 @@ import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.util.List; +import java.util.Iterator; + public class NoTreeForTentContradictionRule extends ContradictionRule { public NoTreeForTentContradictionRule() { @@ -30,9 +34,26 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (cell.getType() != TreeTentType.TENT) { return super.getNoContradictionMessage(); } - int adjTree = treeTentBoard.getAdjacent(cell, TreeTentType.TREE).size(); - int adjUnknown = treeTentBoard.getAdjacent(cell, TreeTentType.UNKNOWN).size(); - if (adjTree == 0 && adjUnknown == 0) { + List adjTrees = treeTentBoard.getAdjacent(cell, TreeTentType.TREE); + List lines = treeTentBoard.getLines(); + for(TreeTentLine l : lines) + { + Iterator i = adjTrees.iterator(); + while(i.hasNext()) + { + TreeTentCell t = i.next(); + if (t.getLocation().equals(l.getC1().getLocation()) && !(cell.getLocation().equals(l.getC2().getLocation()))) + { + i.remove(); + } + if (t.getLocation().equals(l.getC2().getLocation()) && !(cell.getLocation().equals(l.getC2().getLocation()))) + { + i.remove(); + } + } + } + int adjTree = adjTrees.size(); + if (adjTree == 0) { return null; } else { return super.getNoContradictionMessage(); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java index 3c56c12f0..1760e3490 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java @@ -16,22 +16,25 @@ public TouchingTentsContradictionRule() { } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific + * {@link PuzzleElement} index using this rule * * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * @param puzzleElement equivalent {@link PuzzleElement} + * @return null if the transition contains a + * contradiction at the specified puzzleElement, + * otherwise error message. */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { TreeTentBoard treeTentBoard = (TreeTentBoard) board; TreeTentCell cell = (TreeTentCell) puzzleElement; - if (cell.getType() != TreeTentType.TREE) { + if (cell.getType() != TreeTentType.TENT) { return super.getNoContradictionMessage(); } - int adjTree = treeTentBoard.getAdjacent(cell, TreeTentType.TREE).size(); - if (adjTree > 0) { + int adjTree = treeTentBoard.getAdjacent(cell, TreeTentType.TENT).size(); + int diagTree = treeTentBoard.getDiagonals(cell, TreeTentType.TENT).size(); + if (adjTree > 0 || diagTree > 0) { return null; } else { return super.getNoContradictionMessage(); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentBasicRule.java index d1cf01a9b..f68d85e38 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentBasicRule.java @@ -1,10 +1,15 @@ package edu.rpi.legup.puzzle.treetent.rules; +import java.util.List; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.BasicRule; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; public class TreeForTentBasicRule extends BasicRule { public TreeForTentBasicRule() { @@ -24,7 +29,65 @@ public TreeForTentBasicRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; + if (!(puzzleElement instanceof TreeTentLine)) { + return super.getInvalidUseOfRuleMessage() + ": Lines must be created for this rule."; + } + TreeTentBoard board = (TreeTentBoard)transition.getBoard(); + TreeTentLine line = (TreeTentLine)board.getPuzzleElement(puzzleElement); + TreeTentCell tree,tent; + if (line.getC1().getType() == TreeTentType.TREE && line.getC2().getType() == TreeTentType.TENT) { + tree = line.getC1(); + tent = line.getC2(); + } else if (line.getC2().getType() == TreeTentType.TREE && line.getC1().getType() == TreeTentType.TENT) { + tree = line.getC2(); + tent = line.getC1(); + } else { + return super.getInvalidUseOfRuleMessage() + ": This line must connect a tree to a tent."; + } + int forced = isForced(board, tree, tent); + if(forced == 1) + { + return null; + } + else if (forced == -1) + { + return super.getInvalidUseOfRuleMessage() + ": This tent already has a link"; + } + else if (forced == -2) + { + return super.getInvalidUseOfRuleMessage() + ": This tree already has a link"; + } + else + { + return super.getInvalidUseOfRuleMessage() + ": This tree and tent don't need to be linked."; + } + } + + private Integer isForced(TreeTentBoard board, TreeTentCell tree, TreeTentCell tent) + { + List adjTrees = board.getAdjacent(tent, TreeTentType.TREE); + adjTrees.remove(tree); + List lines = board.getLines(); + for(TreeTentLine l : lines) + { + if(l.getC1().getLocation().equals(tree.getLocation()) || l.getC2().getLocation().equals(tree.getLocation())) {return -2;} + for(TreeTentCell c : adjTrees) + { + if(l.getC1().getLocation().equals(c.getLocation())) + { + if(l.getC2().getLocation().equals(tent.getLocation())) {return -1;} + adjTrees.remove(c); + + } + else if(l.getC2().getLocation().equals(c.getLocation())) + { + if(l.getC1().getLocation().equals(tent.getLocation())) {return -1;} + adjTrees.remove(c); + } + } + } + if(adjTrees.size() == 0) {return 1;} + else {return 0;} } /** diff --git a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java new file mode 100644 index 000000000..9f6d242fc --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java @@ -0,0 +1,100 @@ +package edu.rpi.legup.ui; + +import edu.rpi.legup.app.GameBoardFacade; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; + +public class CreatePuzzleDialog extends JDialog implements ActionListener { + private String[] games; + JComboBox gameBox; + JLabel puzzleLabel = new JLabel("Puzzle:"); + JButton ok = new JButton("Ok"); + JButton cancel = new JButton("Cancel"); + JTextField rows; + JTextField columns; + private HomePanel homePanel; + + public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { + super(parent, true); + + this.homePanel = homePanel; + + initPuzzles(); + + Rectangle b = parent.getBounds(); + + setSize(350, 200); + setLocation((int) b.getCenterX() - getWidth() / 2, (int) b.getCenterY() - getHeight() / 2); + + Container c = getContentPane(); + c.setLayout(null); + + puzzleLabel.setBounds(10, 30, 70, 25); + gameBox.setBounds(80, 30, 190, 25); + + ok.setBounds(20, 130, 60, 25); + cancel.setBounds(170, 130, 90, 25); + + c.add(puzzleLabel); + c.add(gameBox); + + rows = new JTextField(); + columns = new JTextField(); + + JLabel rowsLabel = new JLabel("Rows:"); + JLabel columnsLabel = new JLabel("Columns:"); + + rowsLabel.setBounds(30, 70, 60, 25); + columnsLabel.setBounds(30, 95, 60, 25); + + rows.setBounds(100, 70, 60, 25); + columns.setBounds(100, 95, 60, 25); + + c.add(rowsLabel); + c.add(columnsLabel); + + c.add(rows); + c.add(columns); + + c.add(ok); + c.add(cancel); + + ok.addActionListener(this); + cancel.addActionListener(this); + } + + public void initPuzzles() { + this.games = GameBoardFacade.getInstance().getConfig().getFileCreationEnabledPuzzles().toArray(new String[0]); + Arrays.sort(this.games); + gameBox = new JComboBox(games); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == ok) { + String game = (String) gameBox.getSelectedItem(); + try + { + this.homePanel.openEditorWithNewPuzzle(game, Integer.valueOf(this.rows.getText()), Integer.valueOf(this.columns.getText())); + setVisible(false); + } + catch (IllegalArgumentException exception) + { + // Don't do anything. This is here to prevent the dialog from closing if the dimensions are invalid. + } + } + else if (e.getSource() == cancel) { + setVisible(false); + } + } + + private boolean isValidDimensions() + { + // Needs to be implemented + return false; + } +} diff --git a/src/main/java/edu/rpi/legup/ui/DynamicView.java b/src/main/java/edu/rpi/legup/ui/DynamicView.java index e4014debf..671e2d428 100644 --- a/src/main/java/edu/rpi/legup/ui/DynamicView.java +++ b/src/main/java/edu/rpi/legup/ui/DynamicView.java @@ -7,9 +7,7 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseWheelEvent; +import java.awt.event.*; import java.io.IOException; import java.util.Hashtable; import java.util.Objects; @@ -76,6 +74,14 @@ public void mouseWheelMoved(MouseWheelEvent e) { zoomSlider.setPreferredSize(new Dimension(160, 30)); + scrollView.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + zoomSlider.setValue(scrollView.getZoom() ); + zoomLabel.setText(zoomSlider.getValue() + "%"); + } + }); + zoomSlider.addChangeListener((ChangeEvent e) -> { scrollView.zoomTo(zoomSlider.getValue() / 100.0); zoomLabel.setText(zoomSlider.getValue() + "%"); diff --git a/src/main/java/edu/rpi/legup/ui/HomePanel.java b/src/main/java/edu/rpi/legup/ui/HomePanel.java new file mode 100644 index 000000000..48ccea450 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/HomePanel.java @@ -0,0 +1,187 @@ +package edu.rpi.legup.ui; + +import javax.swing.*; +import java.awt.*; + +public class HomePanel extends LegupPanel { + private LegupUI legupUI; + private JFrame frame; + private JButton[] buttons; + private JLabel[] text; + private JMenuBar menuBar; + + private final int buttonSize = 100; + + public HomePanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { + this.legupUI = legupUI; + this.frame = frame; + setLayout(new GridLayout(1, 2)); + initText(); + initButtons(); + } + + public JMenuBar getMenuBar() + { + this.menuBar = new JMenuBar(); + JMenu settings = new JMenu("Settings"); + menuBar.add(settings); + JMenuItem preferences = new JMenuItem("Preferences"); + preferences.addActionListener(a -> { System.out.println("Preferences clicked"); }); + settings.add(preferences); + + JMenuItem about = new JMenuItem("About"); + about.addActionListener(a -> { System.out.println("About clicked"); }); + settings.add(about); + + JMenuItem help = new JMenuItem("Help"); + about.addActionListener(a -> { System.out.println("Help clicked"); }); + settings.add(help); + + JMenuItem contribute = new JMenuItem("Contribute to Legup"); + contribute.addActionListener(a -> { System.out.println("Contribute to Legup clicked"); }); + settings.add(contribute); + + return this.menuBar; + } + + @Override + public void makeVisible() + { + render(); + frame.setJMenuBar(this.getMenuBar()); + } + + private static ImageIcon resizeButtonIcon(ImageIcon icon, int width, int height) + { + Image image = icon.getImage(); + Image resizedImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); + return new ImageIcon(resizedImage); + } + + private void initButtons() { + this.buttons = new JButton[4]; + + this.buttons[0] = new JButton("Open Proof") + { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; + + ImageIcon button0Icon = new ImageIcon("src/main/resources/edu/rpi/legup/homepanel/openproof.png"); + this.buttons[0].setIcon(resizeButtonIcon(button0Icon, this.buttonSize, this.buttonSize)); + this.buttons[0].setHorizontalTextPosition(AbstractButton.CENTER); + this.buttons[0].setVerticalTextPosition(AbstractButton.BOTTOM); + this.buttons[0].addActionListener(l -> this.legupUI.displayPanel(1)); + + this.buttons[1] = new JButton("New Puzzle") + { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; + ImageIcon button1Icon = new ImageIcon("src/main/resources/edu/rpi/legup/homepanel/edit.png"); + this.buttons[1].setIcon(resizeButtonIcon(button1Icon, this.buttonSize, this.buttonSize)); + this.buttons[1].setHorizontalTextPosition(AbstractButton.CENTER); + this.buttons[1].setVerticalTextPosition(AbstractButton.BOTTOM); + this.buttons[1].addActionListener(l -> this.openNewPuzzleDialog()); + + this.buttons[2] = new JButton("Edit Puzzle") + { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; + ImageIcon button2Icon = new ImageIcon("src/main/resources/edu/rpi/legup/homepanel/edit.png"); // PLACEHOLDER + this.buttons[2].setIcon(resizeButtonIcon(button2Icon, this.buttonSize, this.buttonSize)); + this.buttons[2].setHorizontalTextPosition(AbstractButton.CENTER); + this.buttons[2].setVerticalTextPosition(AbstractButton.BOTTOM); + this.buttons[2].addActionListener(l -> this.legupUI.displayPanel(2)); // PLACEHOLDER + + for (int i = 0; i < this.buttons.length - 1; i++) // -1 to avoid the batch grader button + { + //this.buttons[i].setPreferredSize(new Dimension(100, 100)); + this.buttons[i].setBounds(200, 200, 700, 700); + } + + this.buttons[3] = new JButton("Batch Grader"); + this.buttons[3].setHorizontalTextPosition(AbstractButton.CENTER); + this.buttons[3].setVerticalTextPosition(AbstractButton.BOTTOM); + } + + private void initText() + { + this.text = new JLabel[3]; + + JLabel welcome = new JLabel("Welcome to Legup"); + welcome.setFont(new Font("Roboto", Font.BOLD, 23)); + welcome.setAlignmentX(Component.CENTER_ALIGNMENT); + + JLabel version = new JLabel("Version 3.0.0"); // This should be autochanged in the future + version.setFont(new Font("Roboto", Font.ITALIC, 10)); + version.setAlignmentX(Component.CENTER_ALIGNMENT); + + JLabel credits = new JLabel("A project by Dr. Bram van Heuveln"); + credits.setFont(new Font("Roboto", Font.PLAIN, 12)); + credits.setAlignmentX(Component.CENTER_ALIGNMENT); + + this.text[0] = welcome; + this.text[1] = version; + this.text[2] = credits; + } + + private void render() + { + this.removeAll(); + + this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + this.legupUI.setTitle("Legup: A Better Way to Learn Formal Logic"); + + JPanel buttons = new JPanel(); + buttons.add(Box.createRigidArea(new Dimension(5, 0))); + buttons.add(this.buttons[0]); + buttons.add(Box.createRigidArea(new Dimension(5, 0))); + buttons.add(this.buttons[1]); + buttons.add(Box.createRigidArea(new Dimension(5, 0))); + buttons.add(this.buttons[2]); + buttons.add(Box.createRigidArea(new Dimension(5, 0))); + + JPanel batchGraderButton = new JPanel(); + batchGraderButton.add(this.buttons[3]); + batchGraderButton.setAlignmentX(Component.LEFT_ALIGNMENT); + + this.add(Box.createRigidArea(new Dimension(0, 5))); + for (int i = 0; i < this.text.length; i++) + this.add(this.text[i]); + this.add(buttons); + this.add(batchGraderButton); + this.add(Box.createRigidArea(new Dimension(0, 5))); + } + + private void openNewPuzzleDialog() { + CreatePuzzleDialog cpd = new CreatePuzzleDialog(this.frame, this); + cpd.setVisible(true); + } + + public void openEditorWithNewPuzzle(String game, int width, int height) throws IllegalArgumentException { + // Set game type on the puzzle editor + try + { + this.legupUI.displayPanel(2); + this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, width, height); + } + catch (IllegalArgumentException exception) + { + this.legupUI.displayPanel(0); + JOptionPane.showMessageDialog(null, + "The dimensions you entered are invalid. Please double check \n" + + "the number of rows and columns and try again.", + "ERROR: Invalid Dimensions", + JOptionPane.ERROR_MESSAGE); + throw new IllegalArgumentException(exception.getMessage()); + } + } +} diff --git a/src/main/java/edu/rpi/legup/ui/LegupPanel.java b/src/main/java/edu/rpi/legup/ui/LegupPanel.java new file mode 100644 index 000000000..de5f70f76 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/LegupPanel.java @@ -0,0 +1,10 @@ +package edu.rpi.legup.ui; + +import javax.swing.*; + +public abstract class LegupPanel extends JPanel { + /** + * Alerts panel that it will be going visible now + */ + public abstract void makeVisible(); +} diff --git a/src/main/java/edu/rpi/legup/ui/LegupUI.java b/src/main/java/edu/rpi/legup/ui/LegupUI.java index 1c1ac921b..8bca7f663 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupUI.java +++ b/src/main/java/edu/rpi/legup/ui/LegupUI.java @@ -3,114 +3,34 @@ import java.awt.*; import java.awt.event.*; import java.io.*; -import java.net.URI; -import java.net.URL; -import java.io.FileWriter; -import java.io.BufferedWriter; +import java.security.InvalidParameterException; +import java.util.Objects; import javax.swing.*; -import java.util.List; -import java.util.Objects; - import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.app.LegupPreferences; -import edu.rpi.legup.controller.BoardController; -import edu.rpi.legup.controller.RuleController; -import edu.rpi.legup.history.ICommand; -import edu.rpi.legup.history.IHistoryListener; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.ui.lookandfeel.LegupLookAndFeel; -import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.PuzzleExporter; -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.tree.Tree; -import edu.rpi.legup.save.ExportFileException; -import edu.rpi.legup.save.InvalidFileFormatException; import edu.rpi.legup.ui.boardview.BoardView; -import edu.rpi.legup.ui.rulesview.RuleFrame; -import edu.rpi.legup.ui.treeview.TreePanel; -import edu.rpi.legup.ui.treeview.TreeViewSelection; -import edu.rpi.legup.user.Submission; +import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; import edu.rpi.legupupdate.Update; import edu.rpi.legupupdate.UpdateProgress; -import io.grpc.internal.KeepAliveManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import javax.swing.border.TitledBorder; - -public class LegupUI extends JFrame implements WindowListener, IHistoryListener { +public class LegupUI extends JFrame implements WindowListener { private final static Logger LOGGER = LogManager.getLogger(LegupUI.class.getName()); - public static final int ALLOW_HINTS = 1; - public static final int ALLOW_DEFAPP = 2; - public static final int ALLOW_FULLAI = 4; - public static final int ALLOW_JUST = 8; - public static final int REQ_STEP_JUST = 16; - public static final int IMD_FEEDBACK = 32; - public static final int INTERN_RO = 64; - public static final int AUTO_JUST = 128; - - final static int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; - private static final String[] PROFILES = {"No Assistance", "Rigorous Proof", "Casual Proof", "Assisted Proof", "Guided Proof", "Training-Wheels Proof", "No Restrictions"}; - private static final int[] PROF_FLAGS = {0, ALLOW_JUST | REQ_STEP_JUST, ALLOW_JUST, ALLOW_HINTS | ALLOW_JUST | AUTO_JUST, ALLOW_HINTS | ALLOW_JUST | REQ_STEP_JUST, ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_JUST | IMD_FEEDBACK | INTERN_RO, ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_FULLAI | ALLOW_JUST}; - - private static int CONFIG_INDEX = 0; - - protected JPanel contentPane; protected FileDialog fileDialog; - protected JFileChooser folderBrowser; - - protected PickGameDialog pickGameDialog; - protected JButton[] toolBarButtons; - - protected JMenuBar mBar; - - protected JMenu file; - protected JMenuItem newPuzzle, resetPuzzle, saveProof, preferences, exit; - - protected JMenu edit; - protected JMenuItem undo, redo; - - protected JMenu view; - - protected JMenu proof; - protected JMenuItem add, delete, merge, collapse; - protected JCheckBoxMenuItem allowDefault, caseRuleGen, imdFeedback; - - protected JMenu about; - protected JMenuItem checkUpdates, helpLegup, aboutLegup; - - protected JMenu proofMode = new JMenu("Proof Mode"); - protected JCheckBoxMenuItem[] proofModeItems = new JCheckBoxMenuItem[PROF_FLAGS.length]; - - protected JMenu ai = new JMenu("AI"); - protected JMenuItem runAI = new JMenuItem("Run AI to completion"); - protected JMenuItem setpAI = new JMenuItem("Run AI one Step"); - protected JMenuItem testAI = new JMenuItem("Test AI!"); - protected JMenuItem hintAI = new JMenuItem("Hint"); - - protected JMenu help; - - protected JToolBar toolBar; - - protected BoardView boardView; - protected DynamicView dynamicBoardView; - private RuleFrame ruleFrame; - private TreePanel treePanel; - - protected TitledBorder boardBorder; - - protected JSplitPane topHalfPanel, mainPanel; + protected JPanel window; + protected LegupPanel[] panels; /** * Identifies operating system */ - public String getOS() { + public static String getOS() { String os = System.getProperty("os.name").toLowerCase(); if(os.contains("mac")) os = "mac"; else os = "win"; @@ -130,16 +50,12 @@ public LegupUI() { System.err.println("Not supported ui look and feel"); } - this.contentPane = new JPanel(); - contentPane.setLayout(new BorderLayout()); - setContentPane(contentPane); + initPanels(); + displayPanel(0); setIconImage(new ImageIcon(Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource( "edu/rpi/legup/images/Legup/Basic Rules.gif"))).getImage()); - setupMenu(); - setupToolBar(); - setupContent(); if (LegupPreferences.getInstance().getUserPref(LegupPreferences.START_FULL_SCREEN).equals(Boolean.toString(true))) { setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH); } @@ -164,498 +80,45 @@ public void keyTyped(KeyEvent e) { setMinimumSize(getPreferredSize()); } - public static boolean profFlag(int flag) { - return !((PROF_FLAGS[CONFIG_INDEX] & flag) == 0); - } - - public void repaintTree() { - treePanel.repaintTreeView(GameBoardFacade.getInstance().getTree()); - } - - /** - * Sets up the menu bar - */ - private void setupMenu() { - mBar = new JMenuBar(); - fileDialog = new FileDialog(this); - - file = new JMenu("File"); - newPuzzle = new JMenuItem("Open"); - resetPuzzle = new JMenuItem("Reset Puzzle"); -// genPuzzle = new JMenuItem("Puzzle Generators"); - saveProof = new JMenuItem("Save Proof"); - preferences = new JMenuItem("Preferences"); - exit = new JMenuItem("Exit"); - - edit = new JMenu("Edit"); - undo = new JMenuItem("Undo"); - redo = new JMenuItem("Redo"); - - view = new JMenu("View"); - - proof = new JMenu("Proof"); - - String os = getOS(); - - add = new JMenuItem("Add"); - add.addActionListener(a -> treePanel.add()); - if(Objects.equals(os, "mac")) add.setAccelerator(KeyStroke.getKeyStroke('A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else add.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.CTRL_DOWN_MASK)); - proof.add(add); - - delete = new JMenuItem("Delete"); - delete.addActionListener(a -> treePanel.delete()); - if(Objects.equals(os, "mac")) delete.setAccelerator(KeyStroke.getKeyStroke('D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else delete.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); - proof.add(delete); - - merge = new JMenuItem("Merge"); - merge.addActionListener(a -> treePanel.merge()); - if(Objects.equals(os, "mac")) merge.setAccelerator(KeyStroke.getKeyStroke('M', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else merge.setAccelerator(KeyStroke.getKeyStroke('M', InputEvent.CTRL_DOWN_MASK)); - proof.add(merge); - - collapse = new JMenuItem("Collapse"); - collapse.addActionListener(a -> treePanel.collapse()); - if(Objects.equals(os, "mac")) collapse.setAccelerator(KeyStroke.getKeyStroke('C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else collapse.setAccelerator(KeyStroke.getKeyStroke('C', InputEvent.CTRL_DOWN_MASK)); - collapse.setEnabled(false); - proof.add(collapse); - - allowDefault = new JCheckBoxMenuItem("Allow Default Rule Applications", - LegupPreferences.getInstance().getUserPref(LegupPreferences.ALLOW_DEFAULT_RULES).equalsIgnoreCase(Boolean.toString(true))); - allowDefault.addChangeListener(e -> { - LegupPreferences.getInstance().setUserPref(LegupPreferences.ALLOW_DEFAULT_RULES, Boolean.toString(allowDefault.isSelected())); - }); - proof.add(allowDefault); - - caseRuleGen = new JCheckBoxMenuItem("Automatically generate cases for CaseRule", - LegupPreferences.getInstance().getUserPref(LegupPreferences.AUTO_GENERATE_CASES).equalsIgnoreCase(Boolean.toString(true))); - caseRuleGen.addChangeListener(e -> { - LegupPreferences.getInstance().setUserPref(LegupPreferences.AUTO_GENERATE_CASES, Boolean.toString(caseRuleGen.isSelected())); - }); - proof.add(caseRuleGen); - - imdFeedback = new JCheckBoxMenuItem("Provide immediate feedback", - LegupPreferences.getInstance().getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK).equalsIgnoreCase(Boolean.toString(true))); - imdFeedback.addChangeListener(e -> { - LegupPreferences.getInstance().setUserPref(LegupPreferences.IMMEDIATE_FEEDBACK, Boolean.toString(imdFeedback.isSelected())); - }); - proof.add(imdFeedback); - - about = new JMenu("About"); - checkUpdates = new JMenuItem("Check for Updates..."); - helpLegup = new JMenuItem("Help Legup"); - aboutLegup = new JMenuItem("About Legup"); - - // unused - // help = new JMenu("Help"); - - mBar.add(file); - file.add(newPuzzle); - newPuzzle.addActionListener((ActionEvent) -> promptPuzzle()); - if(Objects.equals(os, "mac")) newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); - -// file.add(genPuzzle); -//// genPuzzle.addActionListener((ActionEvent) -> -//// { -//// pickGameDialog = new PickGameDialog(this, true); -//// pickGameDialog.setVisible(true); -//// }); - file.add(resetPuzzle); - resetPuzzle.addActionListener(a -> { - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - if (puzzle != null) { - Tree tree = GameBoardFacade.getInstance().getTree(); - TreeNode rootNode = tree.getRootNode(); - if (rootNode != null) { - int confirmReset = JOptionPane.showConfirmDialog(this, "Reset Puzzle to Root Node?", "Confirm Reset", JOptionPane.YES_NO_CANCEL_OPTION); - if (confirmReset == JOptionPane.YES_OPTION) { - List children = rootNode.getChildren(); - children.forEach(t -> puzzle.notifyTreeListeners(l -> l.onTreeElementRemoved(t))); - final TreeViewSelection selection = new TreeViewSelection(treePanel.getTreeView().getElementView(rootNode)); - puzzle.notifyTreeListeners(l -> l.onTreeSelectionChanged(selection)); - GameBoardFacade.getInstance().getHistory().clear(); - } - } - } - }); - if(Objects.equals(os, "mac")) resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', InputEvent.CTRL_DOWN_MASK)); - file.addSeparator(); - - file.add(saveProof); - saveProof.addActionListener((ActionEvent) -> saveProof()); - if(Objects.equals(os, "mac")) saveProof.setAccelerator(KeyStroke.getKeyStroke('S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else saveProof.setAccelerator(KeyStroke.getKeyStroke('S', InputEvent.CTRL_DOWN_MASK)); - - file.add(preferences); - preferences.addActionListener(a -> { - PreferencesDialog preferencesDialog = new PreferencesDialog(this); - }); - file.addSeparator(); - - file.add(exit); - exit.addActionListener((ActionEvent) -> System.exit(0)); - if(Objects.equals(os, "mac")) exit.setAccelerator(KeyStroke.getKeyStroke('Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); - mBar.add(edit); - - - edit.add(undo); - undo.addActionListener((ActionEvent) -> - { - GameBoardFacade.getInstance().getHistory().undo(); - }); - if(Objects.equals(os, "mac")) undo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - else undo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK)); - - edit.add(redo); - redo.addActionListener((ActionEvent) -> - { - GameBoardFacade.getInstance().getHistory().redo(); - }); - - if(Objects.equals(os, "mac")) redo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + InputEvent.SHIFT_DOWN_MASK)); - else redo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + private void initPanels() { + window = new JPanel(); + window.setLayout(new BorderLayout()); + add(window); + panels = new LegupPanel[3]; - mBar.add(proof); + panels[0] = new HomePanel(this.fileDialog, this, this); + panels[1] = new ProofEditorPanel(this.fileDialog, this, this); + panels[2] = new PuzzleEditorPanel(this.fileDialog, this, this); - about.add(checkUpdates); - checkUpdates.addActionListener(l -> { - checkUpdates(); - }); - checkUpdates.setEnabled(false); - - about.add(aboutLegup); - aboutLegup.addActionListener(l -> { - JOptionPane.showMessageDialog(null, "Version: 2.0.0"); - }); - - about.add(helpLegup); - helpLegup.addActionListener(l -> { - try { - java.awt.Desktop.getDesktop().browse(URI.create("https://github.com/jpoegs/Legup2.0")); - } catch (IOException e) { - LOGGER.error("Can't open web page"); - } - }); - - mBar.add(about); - - setJMenuBar(mBar); } - // contains all the code to setup the toolbar - private void setupToolBar() { - setToolBarButtons(new JButton[ToolbarName.values().length]); - for (int i = 0; i < ToolbarName.values().length; i++) { - String toolBarName = ToolbarName.values()[i].toString(); - URL resourceLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); - assert resourceLocation != null; - JButton button = new JButton(toolBarName, new ImageIcon(resourceLocation)); - button.setFocusPainted(false); - getToolBarButtons()[i] = button; - } - - toolBar = new JToolBar(); - toolBar.setFloatable(false); - toolBar.setRollover(true); - - for (int i = 0; i < getToolBarButtons().length; i++) { - for (int j : TOOLBAR_SEPARATOR_BEFORE) { - if (i == j) { - toolBar.addSeparator(); - } - } - String toolBarName = ToolbarName.values()[i].toString(); - - toolBar.add(getToolBarButtons()[i]); - getToolBarButtons()[i].setToolTipText(toolBarName); - - getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); - getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); + protected void displayPanel(int option) { + if (option > panels.length || option < 0) { + throw new InvalidParameterException("Invalid option"); } - -// toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent e) -> promptPuzzle()); -// toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> saveProof()); -// toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().undo()); -// toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().redo()); - toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> { - }); - toolBarButtons[ToolbarName.CHECK.ordinal()].addActionListener((ActionEvent e) -> checkProof()); - toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> { - }); - toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> { - }); - - toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].addActionListener((ActionEvent e) -> checkProofAll()); - -// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); - toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); - toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(false); - toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); - toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].setEnabled(false); - toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].setEnabled(true); - - contentPane.add(toolBar, BorderLayout.NORTH); - } - - /** - * Sets the main content for the edu.rpi.legup.user interface - */ - protected void setupContent() { -// JPanel consoleBox = new JPanel(new BorderLayout()); - JPanel treeBox = new JPanel(new BorderLayout()); - JPanel ruleBox = new JPanel(new BorderLayout()); - - RuleController ruleController = new RuleController(); - ruleFrame = new RuleFrame(ruleController); - ruleBox.add(ruleFrame, BorderLayout.WEST); - - treePanel = new TreePanel(this); - - dynamicBoardView = new DynamicView(new ScrollView(new BoardController())); - TitledBorder titleBoard = BorderFactory.createTitledBorder("Board"); - titleBoard.setTitleJustification(TitledBorder.CENTER); - dynamicBoardView.setBorder(titleBoard); - - JPanel boardPanel = new JPanel(new BorderLayout()); - topHalfPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, ruleFrame, dynamicBoardView); - mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, topHalfPanel, treePanel); - topHalfPanel.setPreferredSize(new Dimension(600, 400)); - mainPanel.setPreferredSize(new Dimension(600, 600)); - - boardPanel.add(mainPanel); - boardBorder = BorderFactory.createTitledBorder("Board"); - boardBorder.setTitleJustification(TitledBorder.CENTER); - - ruleBox.add(boardPanel); - treeBox.add(ruleBox); - contentPane.add(treeBox); -// consoleBox.add(treeBox); -// -// getContentPane().add(consoleBox); - -// JPopupPanel popupPanel = new JPopupPanel(); -// setGlassPane(popupPanel); -// popupPanel.setVisible(true); - - mainPanel.setDividerLocation(mainPanel.getMaximumDividerLocation() + 100); + this.window.removeAll(); + panels[option].makeVisible(); + this.window.add(panels[option]); pack(); revalidate(); + repaint(); } - /** - * Saves a proof - */ - private void saveProof() { - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - if (puzzle == null) { - return; - } - - fileDialog.setMode(FileDialog.SAVE); - fileDialog.setTitle("Save Proof"); - String curFileName = GameBoardFacade.getInstance().getCurFileName(); - if (curFileName == null) { - fileDialog.setDirectory(LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); - } else { - File curFile = new File(curFileName); - fileDialog.setDirectory(curFile.getParent()); - } - fileDialog.setVisible(true); - - String fileName = null; - if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { - fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); - } - - if (fileName != null) { - try { - PuzzleExporter exporter = puzzle.getExporter(); - if (exporter == null) { - throw new ExportFileException("Puzzle exporter null"); - } - exporter.exportPuzzle(fileName); - } catch (ExportFileException e) { - e.printStackTrace(); - } - } - } - - /** - * Checks the proof for correctness - */ - private void checkProof() { - GameBoardFacade facade = GameBoardFacade.getInstance(); - Tree tree = GameBoardFacade.getInstance().getTree(); - Board board = facade.getBoard(); - Board finalBoard = null; - boolean delayStatus = true; //board.evalDelayStatus(); - - repaintAll(); - - Puzzle puzzle = facade.getPuzzleModule(); - - if (puzzle.isPuzzleComplete()) { - // This is for submission which is not integrated yet - /*int confirm = JOptionPane.showConfirmDialog(null, "Congratulations! Your proof is correct. Would you like to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); - if (confirm == JOptionPane.YES_OPTION) { - Submission submission = new Submission(board); - submission.submit(); - }*/ - JOptionPane.showMessageDialog(null, "Congratulations! Your proof is correct."); - showStatus("Your proof is correct.", false); - } else { - String message = "\nThe game board is not solved."; - JOptionPane.showMessageDialog(null, message, "Invalid proof.", JOptionPane.ERROR_MESSAGE); - - showStatus(message, true); - } - } - - private void traverseDir(File folder, BufferedWriter writer, String path) throws IOException { - // Recursively traverse directory - GameBoardFacade facade = GameBoardFacade.getInstance(); - - // Folder is empty - if(Objects.requireNonNull(folder.listFiles()).length == 0) { - writer.append(path).append(",Empty folder,,Ungradeable\n"); - return; - } - - // Travese directory, recurse if sub-directory found - // If ungradeable, do not leave a score (0, 1) - for(final File f : Objects.requireNonNull(folder.listFiles())) { - // Recurse - if(f.isDirectory()) { - traverseDir(f, writer, path + "/" + f.getName()); - continue; - } - - // Set path name - writer.append(path).append(","); - - // Load puzzle, run checker - // If wrong file type, ungradeable - String fName = f.getName(); - String fPath = f.getAbsolutePath(); - File puzzleFile = new File(fPath); - if(puzzleFile.exists()) { - // Try to load file. If invalid, note in csv - try { - // Load puzzle, run checker - GameBoardFacade.getInstance().loadPuzzle(fPath); - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - setTitle(puzzleName + " - " + puzzleFile.getName()); - facade = GameBoardFacade.getInstance(); - Puzzle puzzle = facade.getPuzzleModule(); - - // Write data - writer.append(fName).append(","); - if(puzzle.isPuzzleComplete()) writer.append("1,Solved\n"); - else writer.append("0,Unsolved\n"); - } catch (InvalidFileFormatException e) { - writer.append(fName).append(" - invalid type,,Ungradeable\n"); - } - } else { - LOGGER.debug("Failed to run sim"); - } - } - } - - /** - * Checks the proof for all files - */ - private void checkProofAll() { - GameBoardFacade facade = GameBoardFacade.getInstance(); - - /* - * Select dir to grade; recursively grade sub-dirs using traverseDir() - * Selected dir must have sub-dirs for each student: - * GradeThis - * | - * | -> Student 1 - * | | - * | | -> Proofs - */ - folderBrowser = new JFileChooser(); - folderBrowser.setCurrentDirectory(new java.io.File(".")); - folderBrowser.setDialogTitle("Select Directory"); - folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderBrowser.setAcceptAllFileFilterUsed(false); - folderBrowser.showOpenDialog(this); - File folder = folderBrowser.getSelectedFile(); - - // Write csv file (Path,File-Name,Score,Solved?) - File resultFile = new File(folder.getAbsolutePath() + File.separator + "result.csv"); - try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { - writer.append("Name,File Name,Score,Solved?\n"); - - // Go through student folders - for (final File folderEntry : Objects.requireNonNull(folder.listFiles(File::isDirectory))) { - // Write path - String path = folderEntry.getName(); - traverseDir(folderEntry, writer, path); - } - }catch (IOException ex){ - LOGGER.error(ex.getMessage()); - } - JOptionPane.showMessageDialog(null, "Batch grading complete."); + public ProofEditorPanel getProofEditor() { + return (ProofEditorPanel) panels[1]; } - - private boolean basicCheckProof(int[][] origCells) { - return false; + public PuzzleEditorPanel getPuzzleEditor() { + return (PuzzleEditorPanel) panels[2]; } - /** - * Submits the proof file - */ - private void submit() { - GameBoardFacade facade = GameBoardFacade.getInstance(); - Board board = facade.getBoard(); - boolean delayStatus = true; //board.evalDelayStatus(); - repaintAll(); - - Puzzle pm = facade.getPuzzleModule(); - if (pm.isPuzzleComplete()) { - // 0 means yes, 1 means no (Java's fault...) - int confirm = JOptionPane.showConfirmDialog(null, "Are you sure you wish to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); - if (confirm == 0) { - Submission submission = new Submission(board); - submission.submit(); - } - } else { - JOptionPane.showConfirmDialog(null, "Your proof is incorrect! Are you sure you wish to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); - Submission submit = new Submission(board); - } + public void repaintTree() { + getProofEditor().repaintTree(); } private void directions() { JOptionPane.showMessageDialog(null, "For every move you make, you must provide a rules for it (located in the Rules panel).\n" + "While working on the edu.rpi.legup.puzzle, you may click on the \"Check\" button to test your proof for correctness.", "Directions", JOptionPane.PLAIN_MESSAGE); } - private void showAll() { -// getToolBarButtons()[ToolbarName.SAVE.ordinal()].setEnabled(true); -// getToolBarButtons()[ToolbarName.UNDO.ordinal()].setEnabled(false); -// getToolBarButtons()[ToolbarName.REDO.ordinal()].setEnabled(false); - getToolBarButtons()[ToolbarName.HINT.ordinal()].setEnabled(true); - getToolBarButtons()[ToolbarName.CHECK.ordinal()].setEnabled(true); - getToolBarButtons()[ToolbarName.SUBMIT.ordinal()].setEnabled(true); - getToolBarButtons()[ToolbarName.DIRECTIONS.ordinal()].setEnabled(true); - - pack(); - } - - private void repaintAll() { - boardView.repaint(); - treePanel.repaint(); - } - public void showStatus(String status, boolean error) { showStatus(status, error, 1); } @@ -664,121 +127,10 @@ public void errorEncountered(String error) { JOptionPane.showMessageDialog(null, error); } - public void reloadGui() { - repaintTree(); - } - - public void promptPuzzle() { - GameBoardFacade facade = GameBoardFacade.getInstance(); - if (facade.getBoard() != null) { - if (noquit("Opening a new puzzle?")) // !noquit or noquit? - { - return; - } - } - - fileDialog.setMode(FileDialog.LOAD); - fileDialog.setTitle("Select Puzzle"); - fileDialog.setVisible(true); - String fileName = null; - File puzzleFile = null; - if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { - fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); - puzzleFile = new File(fileName); - } - - if (puzzleFile != null && puzzleFile.exists()) { - try { - GameBoardFacade.getInstance().loadPuzzle(fileName); - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - setTitle(puzzleName + " - " + puzzleFile.getName()); - } catch (InvalidFileFormatException e) { - LOGGER.error(e.getMessage()); - } - } - } - public void showStatus(String status, boolean error, int timer) { // TODO: implement } - public void checkUpdates() { - String updateStr = null; - File jarPath = null; - try { - jarPath = new File(LegupUI.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile(); - } catch (Exception e) { - updateStr = "An error occurred while attempting to update Legup..."; - JOptionPane.showMessageDialog(this, updateStr, "Update Legup", JOptionPane.ERROR_MESSAGE); - } - Update update = new Update(Update.Stream.CLIENT, jarPath); - - boolean isUpdateAvailable = update.checkUpdate(); - int ans = 0; - if (isUpdateAvailable) { - updateStr = "There is update available. Do you want to update?"; - ans = JOptionPane.showConfirmDialog(this, updateStr, "Update Legup", JOptionPane.OK_CANCEL_OPTION); - } else { - updateStr = "There is no update available at this time. Check again later!"; - JOptionPane.showMessageDialog(this, updateStr, "Update Legup", JOptionPane.INFORMATION_MESSAGE); - } - - if (ans == JOptionPane.OK_OPTION && isUpdateAvailable) { - LOGGER.info("Updating Legup...."); - - new Thread(() -> { - JDialog updateDialog = new JDialog(this, "Updating Legup...", true); - JProgressBar dpb = new JProgressBar(0, 500); - updateDialog.add(BorderLayout.CENTER, dpb); - updateDialog.add(BorderLayout.NORTH, new JLabel("Progress...")); - updateDialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); - updateDialog.setSize(300, 75); - updateDialog.setResizable(false); - updateDialog.setLocationRelativeTo(this); -// updateDialog.setVisible(true); - update.setUpdateProgress(new UpdateProgress() { - double total = 0; - - @Override - public void setTotalDownloads(double total) { - this.total = total; - dpb.setString("0 - " + total); - } - - @Override - public void setCurrentDownload(double current) { - dpb.setValue((int) (current / total * 100)); - dpb.setString(current + " - " + total); - } - - @Override - public void setDescription(String description) { - - } - }); - update.update(); - }).start(); - } /*else { -// if (SystemUtils.IS_OS_WINDOWS) { -// File java = new File(SystemUtils.JAVA_HOME); -// java = new File(java, "bin"); -// java = new File(java, "javaw.exe"); -// String javaPath = java.getPath(); -// String legupPath = LegupUI.class.getProtectionDomain().getCodeSource().getLocation().getPath(); -// try { -// LOGGER.severe("starting new legup"); -// Process process = Runtime.getRuntime().exec(new String[]{javaPath, "-jar", legupPath}); -// process.waitFor(); -// System.out.println(new String(process.getInputStream().readAllBytes())); -// System.err.println(new String(process.getErrorStream().readAllBytes())); -// -// } catch (InterruptedException | IOException e) { -// LOGGER.severe("interrupted or io"); -// } -// } - }*/ - } - //ask to edu.rpi.legup.save current proof public boolean noquit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_CANCEL_OPTION); @@ -822,134 +174,15 @@ public void windowDeactivated(WindowEvent e) { } - /** - * Gets the toolbar buttons - * - * @return toolbar buttons - */ - public JButton[] getToolBarButtons() { - return toolBarButtons; - } - - /** - * Sets the toolbar buttons - * - * @param toolBarButtons toolbar buttons - */ - public void setToolBarButtons(JButton[] toolBarButtons) { - this.toolBarButtons = toolBarButtons; - } - - public void setPuzzleView(Puzzle puzzle) { - this.boardView = puzzle.getBoardView(); - - dynamicBoardView = new DynamicView(boardView); - this.topHalfPanel.setRightComponent(dynamicBoardView); - this.topHalfPanel.setVisible(true); - String boardType = boardView.getBoard().getClass().getSimpleName(); - boardType = boardType.substring(0, boardType.indexOf("Board")); - TitledBorder titleBoard = BorderFactory.createTitledBorder(boardType + " Board"); - titleBoard.setTitleJustification(TitledBorder.CENTER); - dynamicBoardView.setBorder(titleBoard); - - this.treePanel.getTreeView().resetView(); - this.treePanel.getTreeView().setTree(puzzle.getTree()); - - puzzle.addTreeListener(treePanel.getTreeView()); - puzzle.addBoardListener(puzzle.getBoardView()); - - ruleFrame.getBasicRulePanel().setRules(puzzle.getBasicRules()); - ruleFrame.getCasePanel().setRules(puzzle.getCaseRules()); - ruleFrame.getContradictionPanel().setRules(puzzle.getContradictionRules()); - - toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); -// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); - - reloadGui(); - } - public BoardView getBoardView() { - return boardView; + return getProofEditor().getBoardView(); } public DynamicView getDynamicBoardView() { - return dynamicBoardView; + return getProofEditor().getDynamicBoardView(); } public TreePanel getTreePanel() { - return treePanel; - } - - /** - * Called when a action is pushed onto the edu.rpi.legup.history stack - * - * @param command action to push onto the stack - */ - @Override - public void onPushChange(ICommand command) { - LOGGER.info("Pushing " + command.getClass().getSimpleName() + " to stack."); - undo.setEnabled(true); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(true); - redo.setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); - - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); - setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); - } - - /** - * Called when an action is undone - * - * @param isBottom true if there are no more actions to undo, false otherwise - * @param isTop true if there are no more changes to redo, false otherwise - */ - @Override - public void onUndo(boolean isBottom, boolean isTop) { - undo.setEnabled(!isBottom); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); - redo.setEnabled(!isTop); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); - if (isBottom) { - setTitle(puzzleName + " - " + puzzleFile.getName()); - } else { - setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); - } - } - - /** - * Called when an action is redone - * - * @param isBottom true if there are no more actions to undo, false otherwise - * @param isTop true if there are no more changes to redo, false otherwise - */ - @Override - public void onRedo(boolean isBottom, boolean isTop) { - undo.setEnabled(!isBottom); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); - redo.setEnabled(!isTop); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); - if (isBottom) { - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); - setTitle(puzzleName + " - " + puzzleFile.getName()); - } else { - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); - setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); - } - } - - /** - * Called when the history is cleared - */ - @Override - public void onClearHistory() { - undo.setEnabled(false); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); - redo.setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + return getProofEditor().getTreePanel(); } } diff --git a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java index 3e511f1da..78d6cf72d 100644 --- a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java @@ -36,20 +36,16 @@ public class PreferencesDialog extends JDialog { } } - public PreferencesDialog(LegupUI legupUI) { - super(legupUI); + public PreferencesDialog(Frame frame) { + super(frame); setTitle("Preferences"); JPanel mainPanel = new JPanel(); + mainPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); mainPanel.setLayout(new BorderLayout()); - JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.LEFT); - JScrollPane generalTab = createGeneralTab(); - - tabbedPane.addTab("General", generalTab); - - mainPanel.add(tabbedPane, BorderLayout.CENTER); + mainPanel.add(createGeneralTab()); JPanel bottomPanel = new JPanel(); bottomPanel.setBorder(null); @@ -80,7 +76,7 @@ public PreferencesDialog(LegupUI legupUI) { setContentPane(mainPanel); setSize(600, 400); - setLocationRelativeTo(legupUI); + setLocationRelativeTo(frame); setVisible(true); } @@ -182,6 +178,15 @@ private JScrollPane createGeneralTab() { immFeedbackRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, immFeedbackRow.getPreferredSize().height)); contentPane.add(immFeedbackRow); + contentPane.add(createLeftLabel("Instructor Preferences")); + contentPane.add(createLineSeparator()); + immFeedback = new JCheckBox("Instructor Mode", Boolean.valueOf(prefs.getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK))); + immFeedback.setToolTipText("Currently unimplemented, this does nothing right now"); + immFeedbackRow.setLayout(new BorderLayout()); + immFeedbackRow.add(immFeedback, BorderLayout.WEST); + immFeedbackRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, immFeedbackRow.getPreferredSize().height)); + contentPane.add(immFeedbackRow); + scrollPane.setViewportView(contentPane); return scrollPane; } diff --git a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java new file mode 100644 index 000000000..ffeaa994b --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -0,0 +1,763 @@ +package edu.rpi.legup.ui; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.app.LegupPreferences; +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.controller.RuleController; +import edu.rpi.legup.history.ICommand; +import edu.rpi.legup.history.IHistoryListener; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.PuzzleExporter; +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.save.ExportFileException; +import edu.rpi.legup.save.InvalidFileFormatException; +import edu.rpi.legup.ui.boardview.BoardView; +import edu.rpi.legup.ui.proofeditorui.rulesview.RuleFrame; +import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; +import edu.rpi.legup.user.Submission; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.swing.*; +import javax.swing.border.TitledBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Objects; + +public class ProofEditorPanel extends LegupPanel implements IHistoryListener { + private final static Logger LOGGER = LogManager.getLogger(ProofEditorPanel.class.getName()); + private JMenuBar mBar; + private TreePanel treePanel; + private FileDialog fileDialog; + private JFrame frame; + private RuleFrame ruleFrame; + private DynamicView dynamicBoardView; + private JSplitPane topHalfPanel, mainPanel; + private TitledBorder boardBorder; + + private JButton[] toolBarButtons; + private JMenu file; + private JMenuItem newPuzzle, resetPuzzle, saveProof, preferences, exit; + private JMenu edit; + private JMenuItem undo, redo; + + private JMenu view; + + private JMenu proof; + private JMenuItem add, delete, merge, collapse; + private JCheckBoxMenuItem allowDefault, caseRuleGen, imdFeedback; + + private JMenu about, help; + private JMenuItem helpLegup, aboutLegup; + + private JToolBar toolBar; + private BoardView boardView; + private JFileChooser folderBrowser; + + private LegupUI legupUI; + + public static final int ALLOW_HINTS = 1; + public static final int ALLOW_DEFAPP = 2; + public static final int ALLOW_FULLAI = 4; + public static final int ALLOW_JUST = 8; + public static final int REQ_STEP_JUST = 16; + public static final int IMD_FEEDBACK = 32; + public static final int INTERN_RO = 64; + public static final int AUTO_JUST = 128; + final static int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; + private static final String[] PROFILES = {"No Assistance", "Rigorous Proof", "Casual Proof", "Assisted Proof", "Guided Proof", "Training-Wheels Proof", "No Restrictions"}; + private static final int[] PROF_FLAGS = {0, ALLOW_JUST | REQ_STEP_JUST, ALLOW_JUST, ALLOW_HINTS | ALLOW_JUST | AUTO_JUST, ALLOW_HINTS | ALLOW_JUST | REQ_STEP_JUST, ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_JUST | IMD_FEEDBACK | INTERN_RO, ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_FULLAI | ALLOW_JUST}; + private JMenu proofMode = new JMenu("Proof Mode"); + private JCheckBoxMenuItem[] proofModeItems = new JCheckBoxMenuItem[PROF_FLAGS.length]; + + private static int CONFIG_INDEX = 0; + + protected JMenu ai = new JMenu("AI"); + protected JMenuItem runAI = new JMenuItem("Run AI to completion"); + protected JMenuItem setpAI = new JMenuItem("Run AI one Step"); + protected JMenuItem testAI = new JMenuItem("Test AI!"); + protected JMenuItem hintAI = new JMenuItem("Hint"); + + public ProofEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { + this.fileDialog = fileDialog; + this.frame = frame; + this.legupUI = legupUI; + setLayout(new BorderLayout()); + } + + @Override + public void makeVisible() { + this.removeAll(); + + setupToolBar(); + setupContent(); + frame.setJMenuBar(getMenuBar()); + } + + public JMenuBar getMenuBar() { + if(mBar != null) return mBar; + mBar = new JMenuBar(); + + file = new JMenu("File"); + newPuzzle = new JMenuItem("Open"); + resetPuzzle = new JMenuItem("Reset Puzzle"); +// genPuzzle = new JMenuItem("Puzzle Generators"); + saveProof = new JMenuItem("Save Proof"); + preferences = new JMenuItem("Preferences"); + exit = new JMenuItem("Exit"); + + edit = new JMenu("Edit"); + undo = new JMenuItem("Undo"); + redo = new JMenuItem("Redo"); + + view = new JMenu("View"); + + proof = new JMenu("Proof"); + + String os = LegupUI.getOS(); + + add = new JMenuItem("Add"); + add.addActionListener(a -> treePanel.add()); + if(os.equals("mac")) add.setAccelerator(KeyStroke.getKeyStroke('A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else add.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.CTRL_DOWN_MASK)); + proof.add(add); + + delete = new JMenuItem("Delete"); + delete.addActionListener(a -> treePanel.delete()); + if(os.equals("mac")) delete.setAccelerator(KeyStroke.getKeyStroke('D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else delete.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); + proof.add(delete); + + merge = new JMenuItem("Merge"); + merge.addActionListener(a -> treePanel.merge()); + if(os.equals("mac")) merge.setAccelerator(KeyStroke.getKeyStroke('M', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else merge.setAccelerator(KeyStroke.getKeyStroke('M', InputEvent.CTRL_DOWN_MASK)); + proof.add(merge); + + collapse = new JMenuItem("Collapse"); + collapse.addActionListener(a -> treePanel.collapse()); + if(os.equals("mac")) collapse.setAccelerator(KeyStroke.getKeyStroke('C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else collapse.setAccelerator(KeyStroke.getKeyStroke('C', InputEvent.CTRL_DOWN_MASK)); + collapse.setEnabled(false); + proof.add(collapse); + + allowDefault = new JCheckBoxMenuItem("Allow Default Rule Applications", + LegupPreferences.getInstance().getUserPref(LegupPreferences.ALLOW_DEFAULT_RULES).equalsIgnoreCase(Boolean.toString(true))); + allowDefault.addChangeListener(e -> { + LegupPreferences.getInstance().setUserPref(LegupPreferences.ALLOW_DEFAULT_RULES, Boolean.toString(allowDefault.isSelected())); + }); + proof.add(allowDefault); + + caseRuleGen = new JCheckBoxMenuItem("Automatically generate cases for CaseRule", + LegupPreferences.getInstance().getUserPref(LegupPreferences.AUTO_GENERATE_CASES).equalsIgnoreCase(Boolean.toString(true))); + caseRuleGen.addChangeListener(e -> { + LegupPreferences.getInstance().setUserPref(LegupPreferences.AUTO_GENERATE_CASES, Boolean.toString(caseRuleGen.isSelected())); + }); + proof.add(caseRuleGen); + + imdFeedback = new JCheckBoxMenuItem("Provide immediate feedback", + LegupPreferences.getInstance().getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK).equalsIgnoreCase(Boolean.toString(true))); + imdFeedback.addChangeListener(e -> { + LegupPreferences.getInstance().setUserPref(LegupPreferences.IMMEDIATE_FEEDBACK, Boolean.toString(imdFeedback.isSelected())); + }); + proof.add(imdFeedback); + + about = new JMenu("About"); + helpLegup = new JMenuItem("Help Legup"); + aboutLegup = new JMenuItem("About Legup"); + + // unused + // help = new JMenu("Help"); + + mBar.add(file); + file.add(newPuzzle); + newPuzzle.addActionListener((ActionEvent) -> promptPuzzle()); + if(os.equals("mac")) newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); + +// file.add(genPuzzle); +//// genPuzzle.addActionListener((ActionEvent) -> +//// { +//// pickGameDialog = new PickGameDialog(this, true); +//// pickGameDialog.setVisible(true); +//// }); + file.add(resetPuzzle); + resetPuzzle.addActionListener(a -> { + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + if (puzzle != null) { + Tree tree = GameBoardFacade.getInstance().getTree(); + TreeNode rootNode = tree.getRootNode(); + if (rootNode != null) { + int confirmReset = JOptionPane.showConfirmDialog(this, "Reset Puzzle to Root Node?", "Confirm Reset", JOptionPane.YES_NO_CANCEL_OPTION); + if (confirmReset == JOptionPane.YES_OPTION) { + List children = rootNode.getChildren(); + children.forEach(t -> puzzle.notifyTreeListeners(l -> l.onTreeElementRemoved(t))); + final TreeViewSelection selection = new TreeViewSelection(treePanel.getTreeView().getElementView(rootNode)); + puzzle.notifyTreeListeners(l -> l.onTreeSelectionChanged(selection)); + GameBoardFacade.getInstance().getHistory().clear(); + } + } + } + }); + if(os.equals("mac")) resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', InputEvent.CTRL_DOWN_MASK)); + file.addSeparator(); + + file.add(saveProof); + saveProof.addActionListener((ActionEvent) -> saveProof()); + if(os.equals("mac")) saveProof.setAccelerator(KeyStroke.getKeyStroke('S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else saveProof.setAccelerator(KeyStroke.getKeyStroke('S', InputEvent.CTRL_DOWN_MASK)); + + file.add(preferences); + preferences.addActionListener(a -> { + PreferencesDialog preferencesDialog = new PreferencesDialog(this.frame); + }); + file.addSeparator(); + + file.add(exit); + exit.addActionListener((ActionEvent) -> this.legupUI.displayPanel(0)); + if(os.equals("mac")) exit.setAccelerator(KeyStroke.getKeyStroke('Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); + mBar.add(edit); + + + edit.add(undo); + undo.addActionListener((ActionEvent) -> + { + GameBoardFacade.getInstance().getHistory().undo(); + }); + if(os.equals("mac")) undo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else undo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK)); + + edit.add(redo); + redo.addActionListener((ActionEvent) -> + { + GameBoardFacade.getInstance().getHistory().redo(); + }); + if(os.equals("mac")) redo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + InputEvent.SHIFT_DOWN_MASK)); + else redo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + + mBar.add(proof); + + about.add(aboutLegup); + aboutLegup.addActionListener(l -> { + JOptionPane.showMessageDialog(null, "Version: 2.0.0"); + }); + + about.add(helpLegup); + helpLegup.addActionListener(l -> { + try { + java.awt.Desktop.getDesktop().browse(URI.create("https://github.com/jpoegs/Legup2.0")); + } catch (IOException e) { + LOGGER.error("Can't open web page"); + } + }); + + mBar.add(about); + + return mBar; + } + + public void promptPuzzle() { + GameBoardFacade facade = GameBoardFacade.getInstance(); + if (facade.getBoard() != null) { + if (noquit("Opening a new puzzle?")) // !noquit or noquit? + { + return; + } + } + + folderBrowser = new JFileChooser(); + + folderBrowser.showOpenDialog(this); + folderBrowser.setVisible(true); + folderBrowser.setCurrentDirectory(new java.io.File(".")); + folderBrowser.setDialogTitle("Select Directory"); + folderBrowser.setFileSelectionMode(JFileChooser.FILES_ONLY); + folderBrowser.setAcceptAllFileFilterUsed(true); + + String fileName = null; + File puzzleFile = folderBrowser.getSelectedFile(); + if (folderBrowser.getCurrentDirectory() != null && folderBrowser.getSelectedFile().getName() != null) { + fileName = puzzleFile.getAbsolutePath()+ File.separator; + puzzleFile = new File(fileName); + } + + if (puzzleFile != null && puzzleFile.exists()) { + try { + GameBoardFacade.getInstance().loadPuzzle(fileName); + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + frame.setTitle(puzzleName + " - " + puzzleFile.getName()); + } catch (InvalidFileFormatException e) { + LOGGER.error(e.getMessage()); + if (e.getMessage().contains("Proof Tree construction error: could not find rule by ID")) // TO DO: make error message not hardcoded + JOptionPane.showMessageDialog(null, "This file runs on an outdated version of Legup\nand is not compatible with the current version.", "Error", JOptionPane.ERROR_MESSAGE); + else + JOptionPane.showMessageDialog(null, "File does not exist or it cannot be read", "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + + /** + * Saves a proof + */ + private void saveProof() { + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + if (puzzle == null) { + return; + } + + fileDialog.setMode(FileDialog.SAVE); + fileDialog.setTitle("Save Proof"); + String curFileName = GameBoardFacade.getInstance().getCurFileName(); + if (curFileName == null) { + fileDialog.setDirectory(LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); + } else { + File curFile = new File(curFileName); + fileDialog.setDirectory(curFile.getParent()); + } + fileDialog.setVisible(true); + + String fileName = null; + if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { + fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); + } + + if (fileName != null) { + try { + PuzzleExporter exporter = puzzle.getExporter(); + if (exporter == null) { + throw new ExportFileException("Puzzle exporter null"); + } + exporter.exportPuzzle(fileName); + } catch (ExportFileException e) { + e.printStackTrace(); + } + } + } + + //ask to edu.rpi.legup.save current proof + public boolean noquit(String instr) { + int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_CANCEL_OPTION); + return n != JOptionPane.YES_OPTION; + } + + /** + * Sets the main content for the edu.rpi.legup.user interface + */ + protected void setupContent() { +// JPanel consoleBox = new JPanel(new BorderLayout()); + JPanel treeBox = new JPanel(new BorderLayout()); + JPanel ruleBox = new JPanel(new BorderLayout()); + + RuleController ruleController = new RuleController(); + ruleFrame = new RuleFrame(ruleController); + ruleBox.add(ruleFrame, BorderLayout.WEST); + + treePanel = new TreePanel(); + + dynamicBoardView = new DynamicView(new ScrollView(new BoardController())); + TitledBorder titleBoard = BorderFactory.createTitledBorder("Board"); + titleBoard.setTitleJustification(TitledBorder.CENTER); + dynamicBoardView.setBorder(titleBoard); + + JPanel boardPanel = new JPanel(new BorderLayout()); + topHalfPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, ruleFrame, dynamicBoardView); + mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, topHalfPanel, treePanel); + topHalfPanel.setPreferredSize(new Dimension(600, 400)); + mainPanel.setPreferredSize(new Dimension(600, 600)); + + boardPanel.add(mainPanel); + boardBorder = BorderFactory.createTitledBorder("Board"); + boardBorder.setTitleJustification(TitledBorder.CENTER); + + ruleBox.add(boardPanel); + treeBox.add(ruleBox); + this.add(treeBox); +// consoleBox.add(treeBox); +// +// getContentPane().add(consoleBox); + +// JPopupPanel popupPanel = new JPopupPanel(); +// setGlassPane(popupPanel); +// popupPanel.setVisible(true); + + mainPanel.setDividerLocation(mainPanel.getMaximumDividerLocation() + 100); +// frame.pack(); + revalidate(); + } + + private void setupToolBar() { + setToolBarButtons(new JButton[ToolbarName.values().length]); + for (int i = 0; i < ToolbarName.values().length; i++) { + String toolBarName = ToolbarName.values()[i].toString(); + URL resourceLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); + JButton button = new JButton(toolBarName, new ImageIcon(resourceLocation)); + button.setFocusPainted(false); + getToolBarButtons()[i] = button; + } + + toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); + + for (int i = 0; i < getToolBarButtons().length; i++) { + for (int s = 0; s < TOOLBAR_SEPARATOR_BEFORE.length; s++) { + if (i == TOOLBAR_SEPARATOR_BEFORE[s]) { + toolBar.addSeparator(); + } + } + String toolBarName = ToolbarName.values()[i].toString(); + + toolBar.add(getToolBarButtons()[i]); + getToolBarButtons()[i].setToolTipText(toolBarName); + + getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); + getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); + } + +// toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent e) -> promptPuzzle()); +// toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> saveProof()); +// toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().undo()); +// toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().redo()); + toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> { + }); + toolBarButtons[ToolbarName.CHECK.ordinal()].addActionListener((ActionEvent e) -> checkProof()); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> { + }); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> { + }); + + toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].addActionListener((ActionEvent e) -> checkProofAll()); + +// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); +// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); +// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].setEnabled(true); + + this.add(toolBar, BorderLayout.NORTH); + } + + /** + * Sets the toolbar buttons + * + * @param toolBarButtons toolbar buttons + */ + public void setToolBarButtons(JButton[] toolBarButtons) { + this.toolBarButtons = toolBarButtons; + } + + /** + * Gets the toolbar buttons + * + * @return toolbar buttons + */ + public JButton[] getToolBarButtons() { + return toolBarButtons; + } + + /** + * Checks the proof for correctness + */ + private void checkProof() { + GameBoardFacade facade = GameBoardFacade.getInstance(); + Tree tree = GameBoardFacade.getInstance().getTree(); + Board board = facade.getBoard(); + Board finalBoard = null; + boolean delayStatus = true; //board.evalDelayStatus(); + + repaintAll(); + + Puzzle puzzle = facade.getPuzzleModule(); + + if (puzzle.isPuzzleComplete()) { + // This is for submission which is not integrated yet + /*int confirm = JOptionPane.showConfirmDialog(null, "Congratulations! Your proof is correct. Would you like to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); + if (confirm == JOptionPane.YES_OPTION) { + Submission submission = new Submission(board); + submission.submit(); + }*/ + JOptionPane.showMessageDialog(null, "Congratulations! Your proof is correct."); + } else { + String message = "\nThe game board is not solved."; + JOptionPane.showMessageDialog(null, message, "Invalid proof.", JOptionPane.ERROR_MESSAGE); + } + } + + private void repaintAll() { + boardView.repaint(); + treePanel.repaint(); + } + + public void setPuzzleView(Puzzle puzzle) { + this.boardView = puzzle.getBoardView(); + + dynamicBoardView = new DynamicView(boardView); + this.topHalfPanel.setRightComponent(dynamicBoardView); + this.topHalfPanel.setVisible(true); + String boardType = boardView.getBoard().getClass().getSimpleName(); + boardType = boardType.substring(0, boardType.indexOf("Board")); + TitledBorder titleBoard = BorderFactory.createTitledBorder(boardType + " Board"); + titleBoard.setTitleJustification(TitledBorder.CENTER); + dynamicBoardView.setBorder(titleBoard); + + this.treePanel.getTreeView().resetView(); + this.treePanel.getTreeView().setTree(puzzle.getTree()); + + puzzle.addTreeListener(treePanel.getTreeView()); + puzzle.addBoardListener(puzzle.getBoardView()); + + ruleFrame.getBasicRulePanel().setRules(puzzle.getBasicRules()); + ruleFrame.getCasePanel().setRules(puzzle.getCaseRules()); + ruleFrame.getContradictionPanel().setRules(puzzle.getContradictionRules()); + + toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); +// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); + + reloadGui(); + } + + public void reloadGui() { + repaintTree(); + } + + public void repaintTree() { + treePanel.repaintTreeView(GameBoardFacade.getInstance().getTree()); + } + + /** + * Checks the proof for all files + */ + private void checkProofAll() { + GameBoardFacade facade = GameBoardFacade.getInstance(); + + /* + * Select dir to grade; recursively grade sub-dirs using traverseDir() + * Selected dir must have sub-dirs for each student: + * GradeThis + * | + * | -> Student 1 + * | | + * | | -> Proofs + */ + folderBrowser = new JFileChooser(); + + folderBrowser.showOpenDialog(this); + folderBrowser.setVisible(true); + folderBrowser.setCurrentDirectory(new java.io.File(".")); + folderBrowser.setDialogTitle("Select Directory"); + folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + folderBrowser.setAcceptAllFileFilterUsed(false); + + File folder = folderBrowser.getSelectedFile(); + + // Write csv file (Path,File-Name,Puzzle-Type,Score,Solved?) + File resultFile = new File(folder.getAbsolutePath() + File.separator + "result.csv"); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { + writer.append("Name,File Name,Puzzle Type,Score,Solved?\n"); + + // Go through student folders + for (final File folderEntry : Objects.requireNonNull(folder.listFiles(File::isDirectory))) { + // Write path + String path = folderEntry.getName(); + traverseDir(folderEntry, writer, path); + } + }catch (IOException ex){ + LOGGER.error(ex.getMessage()); + } + JOptionPane.showMessageDialog(null, "Batch grading complete."); + } + + private boolean basicCheckProof(int[][] origCells) { + return false; + } + + private void traverseDir(File folder, BufferedWriter writer, String path) throws IOException { + // Recursively traverse directory + GameBoardFacade facade = GameBoardFacade.getInstance(); + + // Folder is empty + if(Objects.requireNonNull(folder.listFiles()).length == 0) { + writer.append(path).append(",Empty folder,,Ungradeable\n"); + return; + } + + // Travese directory, recurse if sub-directory found + // If ungradeable, do not leave a score (0, 1) + for(final File f : Objects.requireNonNull(folder.listFiles())) { + // Recurse + if(f.isDirectory()) { + traverseDir(f, writer, path + "/" + f.getName()); + continue; + } + + // Set path name + writer.append(path).append(","); + + // Load puzzle, run checker + // If wrong file type, ungradeable + String fName = f.getName(); + String fPath = f.getAbsolutePath(); + File puzzleFile = new File(fPath); + if(puzzleFile.exists()) { + // Try to load file. If invalid, note in csv + try { + // Load puzzle, run checker + GameBoardFacade.getInstance().loadPuzzle(fPath); + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + frame.setTitle(puzzleName + " - " + puzzleFile.getName()); + facade = GameBoardFacade.getInstance(); + Puzzle puzzle = facade.getPuzzleModule(); + + // Write data + writer.append(fName).append(","); + writer.append(puzzle.getName()).append(","); + if (puzzle.isPuzzleComplete()) + writer.append("1,Solved\n"); + else + writer.append("0,Unsolved\n"); + } catch (InvalidFileFormatException e) { + writer.append(fName).append(",Invalid,,Ungradeable\n"); + } + } else { + LOGGER.debug("Failed to run sim"); + } + } + } + + public BoardView getBoardView() { + return boardView; + } + + public DynamicView getDynamicBoardView() { + return dynamicBoardView; + } + + public TreePanel getTreePanel() { + return treePanel; + } + + /** + * Called when a action is pushed onto the edu.rpi.legup.history stack + * + * @param command action to push onto the stack + */ + @Override + public void onPushChange(ICommand command) { + LOGGER.info("Pushing " + command.getClass().getSimpleName() + " to stack."); + undo.setEnabled(true); +// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(true); + redo.setEnabled(false); +// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); + frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); + } + + /** + * Called when the history is cleared + */ + @Override + public void onClearHistory() { + undo.setEnabled(false); +// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + redo.setEnabled(false); +// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + } + + /** + * Called when an action is redone + * + * @param isBottom true if there are no more actions to undo, false otherwise + * @param isTop true if there are no more changes to redo, false otherwise + */ + @Override + public void onRedo(boolean isBottom, boolean isTop) { + undo.setEnabled(!isBottom); +// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); + redo.setEnabled(!isTop); +// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); + if (isBottom) { + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); + frame.setTitle(puzzleName + " - " + puzzleFile.getName()); + } else { + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); + frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); + } + } + + /** + * Called when an action is undone + * + * @param isBottom true if there are no more actions to undo, false otherwise + * @param isTop true if there are no more changes to redo, false otherwise + */ + @Override + public void onUndo(boolean isBottom, boolean isTop) { + undo.setEnabled(!isBottom); +// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); + redo.setEnabled(!isTop); +// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); + if (isBottom) { + frame.setTitle(puzzleName + " - " + puzzleFile.getName()); + } else { + frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); + } + } + + /** + * Submits the proof file + */ + private void submit() { + GameBoardFacade facade = GameBoardFacade.getInstance(); + Board board = facade.getBoard(); + boolean delayStatus = true; //board.evalDelayStatus(); + repaintAll(); + + Puzzle pm = facade.getPuzzleModule(); + if (pm.isPuzzleComplete() && delayStatus) { + // 0 means yes, 1 means no (Java's fault...) + int confirm = JOptionPane.showConfirmDialog(null, "Are you sure you wish to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); + if (confirm == 0) { + Submission submission = new Submission(board); + submission.submit(); + } + } else { + JOptionPane.showConfirmDialog(null, "Your proof is incorrect! Are you sure you wish to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); + Submission submit = new Submission(board); + } + } + + private void directions() { + JOptionPane.showMessageDialog(null, "For every move you make, you must provide a rule for it (located in the Rules panel).\n" + "While working on the edu.rpi.legup.puzzle, you may click on the \"Check\" button to test your proof for correctness.", "Directions", JOptionPane.PLAIN_MESSAGE); + } + + public void errorEncountered(String error) { + JOptionPane.showMessageDialog(null, error); + } + + public void showStatus(String status, boolean error, int timer) { + // TODO: implement + } +} diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleCreatorView.java b/src/main/java/edu/rpi/legup/ui/PuzzleCreatorView.java deleted file mode 100644 index 3e5877890..000000000 --- a/src/main/java/edu/rpi/legup/ui/PuzzleCreatorView.java +++ /dev/null @@ -1,17 +0,0 @@ -package edu.rpi.legup.ui; - -import edu.rpi.legup.ui.boardview.BoardView; - -import javax.swing.*; - -public class PuzzleCreatorView extends JFrame { - - private BoardView boardView; - - public PuzzleCreatorView(BoardView boardView) { - this.boardView = boardView; - - setContentPane(new DynamicView(boardView)); - } - -} diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java new file mode 100644 index 000000000..92dd83df9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -0,0 +1,283 @@ +package edu.rpi.legup.ui; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.controller.EditorElementController; +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.history.ICommand; +import edu.rpi.legup.history.IHistoryListener; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.save.InvalidFileFormatException; +import edu.rpi.legup.ui.boardview.BoardView; +import edu.rpi.legup.ui.puzzleeditorui.elementsview.ElementFrame; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.swing.*; +import javax.swing.border.TitledBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.io.File; +import java.net.URL; + +public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { + + private final static Logger LOGGER = LogManager.getLogger(PuzzleEditorPanel.class.getName()); + private JMenu[] menus; + private JMenuBar menuBar; + private JToolBar toolBar; + private JFrame frame; + private JButton[] buttons; + JSplitPane splitPanel; + private JButton[] toolBarButtons; + private JPanel elementPanel; + private DynamicView dynamicBoardView; + private BoardView boardView; + private TitledBorder boardBorder; + //private JSplitPane splitPanel, topHalfPanel; + private FileDialog fileDialog; + private JMenuItem undo, redo; + private ElementFrame elementFrame; + private JPanel treePanel; + private LegupUI legupUI; + final static int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; + + public PuzzleEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { + this.fileDialog = fileDialog; + this.frame = frame; + this.legupUI = legupUI; + setLayout(new BorderLayout()); + } + + protected void setupContent() { + JSplitPane splitPanel; + JPanel elementBox = new JPanel(new BorderLayout()); + + EditorElementController elementController = new EditorElementController(); + elementFrame = new ElementFrame(elementController); + elementBox.add(elementFrame, BorderLayout.WEST); + + dynamicBoardView = new DynamicView(new ScrollView(new BoardController())); + TitledBorder titleBoard = BorderFactory.createTitledBorder("Board"); + titleBoard.setTitleJustification(TitledBorder.CENTER); + dynamicBoardView.setBorder(titleBoard); + + JPanel boardPanel = new JPanel(new BorderLayout()); + splitPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, elementFrame, dynamicBoardView); + splitPanel.setPreferredSize(new Dimension(600, 400)); + + boardPanel.add(splitPanel); + boardBorder = BorderFactory.createTitledBorder("Board"); + boardBorder.setTitleJustification(TitledBorder.CENTER); + + elementBox.add(boardPanel); + this.add(elementBox); + + splitPanel.setDividerLocation(splitPanel.getMaximumDividerLocation()+100); + this.splitPanel = splitPanel; + revalidate(); + } + + public void setMenuBar() { + String os = LegupUI.getOS(); + menuBar = new JMenuBar(); + menus = new JMenu[3]; + + // create menus + + // FILE + menus[0] = new JMenu("File"); + // file>new + JMenuItem newPuzzle = new JMenuItem("New"); + newPuzzle.addActionListener((ActionEvent) -> promptPuzzle()); + if(os.equals("mac")) newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); + // file>save + JMenuItem savePuzzle = new JMenuItem("Save"); + JMenuItem exit = new JMenuItem("Exit"); + exit.addActionListener((ActionEvent) -> this.legupUI.displayPanel(0)); + if (os.equals("mac")) + exit.setAccelerator(KeyStroke.getKeyStroke('Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + else + exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); + menus[0].add(newPuzzle); + menus[0].add(savePuzzle); + menus[0].add(exit); + + // EDIT + menus[1] = new JMenu("Edit"); + // edit>undo + undo = new JMenuItem("Undo"); + // edit>redo + redo = new JMenuItem("Redo"); + + menus[1].add(undo); + menus[1].add(redo); + + // HELP + menus[2] = new JMenu("Help"); + + // add menus to menubar + for (JMenu menu : menus) { + menuBar.add(menu); + } + frame.setJMenuBar(menuBar); + } + + @Override + public void makeVisible() { + this.removeAll(); + + setupToolBar(); + setupContent(); + setMenuBar(); + } + + private void setupToolBar() { + setToolBarButtons(new JButton[ToolbarName.values().length]); + for (int i = 0; i < ToolbarName.values().length; i++) { + String toolBarName = ToolbarName.values()[i].toString(); + URL resourceLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); + JButton button = new JButton(toolBarName, new ImageIcon(resourceLocation)); + button.setFocusPainted(false); + getToolBarButtons()[i] = button; + } + toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); + + for (int i = 0; i < getToolBarButtons().length; i++) { + for (int s = 0; s < TOOLBAR_SEPARATOR_BEFORE.length; s++) { + if (i == TOOLBAR_SEPARATOR_BEFORE[s]) { + toolBar.addSeparator(); + } + } + String toolBarName = ToolbarName.values()[i].toString(); + + toolBar.add(getToolBarButtons()[i]); + getToolBarButtons()[i].setToolTipText(toolBarName); + + getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); + getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); + } + +// toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent e) -> promptPuzzle()); +// toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> saveProof()); +// toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().undo()); +// toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().redo()); + toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> { + }); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> { + }); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> { + }); + +// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); +// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); +// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].setEnabled(false); + + this.add(toolBar, BorderLayout.NORTH); + } + public void loadPuzzleFromHome(String game, int rows, int columns) throws IllegalArgumentException { + GameBoardFacade facade = GameBoardFacade.getInstance(); + try { + facade.loadPuzzle(game, rows, columns); + } + catch (IllegalArgumentException exception) + { + throw new IllegalArgumentException(exception.getMessage()); + } + catch (RuntimeException e){ + LOGGER.error(e.getMessage()); + } + } + public void promptPuzzle() { + GameBoardFacade facade = GameBoardFacade.getInstance(); + if (facade.getBoard() != null) { + if (noQuit("Opening a new puzzle to edit?")) // !noquit or noquit? + { + return; + } + } + + fileDialog.setMode(FileDialog.LOAD); + fileDialog.setTitle("Select Puzzle"); + fileDialog.setVisible(true); + String fileName = null; + File puzzleFile = null; + if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { + fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); + puzzleFile = new File(fileName); + } + + if (puzzleFile != null && puzzleFile.exists()) { + try { + GameBoardFacade.getInstance().loadPuzzle(fileName); + String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + frame.setTitle(puzzleName + " - " + puzzleFile.getName()); + } catch (InvalidFileFormatException e) { + LOGGER.error(e.getMessage()); + } + } + } + public boolean noQuit(String instr) { + int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_CANCEL_OPTION); + return n != JOptionPane.YES_OPTION; + } + + @Override + public void onPushChange(ICommand command) { + + } + + @Override + public void onUndo(boolean isBottom, boolean isTop) { + + } + + @Override + public void onRedo(boolean isBottom, boolean isTop) { + + } + + @Override + public void onClearHistory() { + + } + + public JButton[] getToolBarButtons() { + return toolBarButtons; + } + public void setToolBarButtons(JButton[] toolBarButtons) { + this.toolBarButtons = toolBarButtons; + } + + private void repaintAll() { + boardView.repaint(); + } + + public void setPuzzleView(Puzzle puzzle) { + this.boardView = puzzle.getBoardView(); + dynamicBoardView = new DynamicView(boardView); + + this.splitPanel.setRightComponent(dynamicBoardView); + this.splitPanel.setVisible(true); + + TitledBorder titleBoard = BorderFactory.createTitledBorder(boardView.getClass().getSimpleName()); + titleBoard.setTitleJustification(TitledBorder.CENTER); + dynamicBoardView.setBorder(titleBoard); + + puzzle.addBoardListener(puzzle.getBoardView()); + + elementFrame.getNonPlaceableElementPanel().setElements(puzzle.getNonPlaceableElements()); + elementFrame.getPlaceableElementPanel().setElements(puzzle.getPlaceableElements()); + + toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); +// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); + } + +} diff --git a/src/main/java/edu/rpi/legup/ui/ScrollView.java b/src/main/java/edu/rpi/legup/ui/ScrollView.java index 922b0ec9a..807d95e06 100644 --- a/src/main/java/edu/rpi/legup/ui/ScrollView.java +++ b/src/main/java/edu/rpi/legup/ui/ScrollView.java @@ -305,4 +305,20 @@ protected void draw(Graphics2D graphics2D) { graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); canvas.paint(graphics2D); } + + /** + * Scroll up or down on the ScrollView + * + * @param mag The magnitude for scroll up + * positive is scroll up, negative is scroll down, + * recommend to use getWheelRotation() as the mag + * */ + public void scroll(int mag){ + Point point = super.viewport.getViewPosition(); + // the point changing speed changes with the scale + point.y += mag*getZoom()/20; + viewport.setViewPosition(point); + updateSize(); + revalidate(); + } } diff --git a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java index 435521148..dded8ce23 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java @@ -36,24 +36,24 @@ public ZoomWidget(ScrollView parent) { /** * */ - private class PopupSlider extends JPopupMenu implements ChangeListener { - private static final long serialVersionUID = 8225019381200459814L; + private class PopupSlider extends JPopupMenu implements ChangeListener { + private static final long serialVersionUID = 8225019381200459814L; - private JSlider slider; + private JSlider slider; - public PopupSlider() { - slider = new JSlider(SwingConstants.VERTICAL, 25, 400, 100); - slider.setMajorTickSpacing(25); - slider.setPaintTicks(true); + public PopupSlider() { + slider = new JSlider(SwingConstants.VERTICAL, 0, 400, 200); + slider.setMajorTickSpacing(25); + slider.setPaintTicks(true); - add(slider); - slider.addChangeListener(this); - } + add(slider); + slider.addChangeListener(this); + } - public void stateChanged(ChangeEvent e) { - if (slider.getValueIsAdjusting()) { - parent.zoomTo((double) slider.getValue() / 100.0); + public void stateChanged(ChangeEvent e) { + if (slider.getValueIsAdjusting()) { + parent.zoomTo((double) slider.getValue() / 100.0); + } } } - } } diff --git a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java index 6b352370f..e851d14d7 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java @@ -5,7 +5,6 @@ import java.awt.Color; import java.awt.Dimension; -import java.awt.Point; public class GridBoardView extends BoardView { protected Dimension gridSize; diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/BasicRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/BasicRulePanel.java similarity index 93% rename from src/main/java/edu/rpi/legup/ui/rulesview/BasicRulePanel.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/BasicRulePanel.java index 6307b81d5..4e9bbd62a 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/BasicRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/BasicRulePanel.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/CaseRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java similarity index 91% rename from src/main/java/edu/rpi/legup/ui/rulesview/CaseRulePanel.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java index 534825dc7..5f22ac62c 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/CaseRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import javax.swing.ImageIcon; diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/CaseRuleSelectionView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java similarity index 90% rename from src/main/java/edu/rpi/legup/ui/rulesview/CaseRuleSelectionView.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java index eda8e0cb9..276583a99 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/CaseRuleSelectionView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import edu.rpi.legup.ui.WrapLayout; diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/ContradictionRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java similarity index 91% rename from src/main/java/edu/rpi/legup/ui/rulesview/ContradictionRulePanel.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java index 4b81b6ade..908ffc49b 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/ContradictionRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import javax.swing.*; diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/RuleButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java similarity index 93% rename from src/main/java/edu/rpi/legup/ui/rulesview/RuleButton.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java index ca898a25c..12cae7de1 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/RuleButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import edu.rpi.legup.model.rules.Rule; diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/RuleFrame.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java similarity index 88% rename from src/main/java/edu/rpi/legup/ui/rulesview/RuleFrame.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java index ca0c4ba0b..cfe3ba3b4 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/RuleFrame.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import edu.rpi.legup.controller.RuleController; import edu.rpi.legup.model.Puzzle; @@ -41,14 +41,19 @@ public RuleFrame(RuleController controller) { this.buttonGroup = new ButtonGroup(); basicRulePanel = new BasicRulePanel(this); - - tabbedPane.addTab(basicRulePanel.getName(), basicRulePanel.getIcon(), new JScrollPane(basicRulePanel), basicRulePanel.getToolTip()); + JScrollPane newbrp = new JScrollPane(basicRulePanel); + newbrp.getVerticalScrollBar().setUnitIncrement(16); + tabbedPane.addTab(basicRulePanel.getName(), basicRulePanel.getIcon(), newbrp, basicRulePanel.getToolTip()); casePanel = new CaseRulePanel(this); - tabbedPane.addTab(casePanel.name, casePanel.icon, new JScrollPane(casePanel), casePanel.toolTip); + JScrollPane newcp = new JScrollPane(casePanel); + newcp.getVerticalScrollBar().setUnitIncrement(16); + tabbedPane.addTab(casePanel.name, casePanel.icon, newcp, casePanel.toolTip); contradictionPanel = new ContradictionRulePanel(this); - tabbedPane.addTab(contradictionPanel.name, contradictionPanel.icon, new JScrollPane(contradictionPanel), contradictionPanel.toolTip); + JScrollPane newp = new JScrollPane(contradictionPanel); + newp.getVerticalScrollBar().setUnitIncrement(16); + tabbedPane.addTab(contradictionPanel.name, contradictionPanel.icon, newp, contradictionPanel.toolTip); setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); diff --git a/src/main/java/edu/rpi/legup/ui/rulesview/RulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java similarity index 98% rename from src/main/java/edu/rpi/legup/ui/rulesview/RulePanel.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java index de01f3916..c6bc2ee0a 100644 --- a/src/main/java/edu/rpi/legup/ui/rulesview/RulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.rulesview; +package edu.rpi.legup.ui.proofeditorui.rulesview; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.ui.WrapLayout; diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeElementView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java similarity index 98% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeElementView.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java index 3c46c5241..676992654 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeElementView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.model.tree.TreeElementType; diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeNodeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java similarity index 99% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeNodeView.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java index 55d586f4d..f60ffe300 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeNodeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import edu.rpi.legup.model.rules.RuleType; import edu.rpi.legup.model.tree.TreeElementType; diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java similarity index 96% rename from src/main/java/edu/rpi/legup/ui/treeview/TreePanel.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java index 31c8de096..306cffec8 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.TreeController; @@ -7,7 +7,6 @@ import edu.rpi.legup.history.ICommand; import edu.rpi.legup.history.MergeCommand; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.Tree; import edu.rpi.legup.ui.DynamicView; import edu.rpi.legup.ui.LegupUI; @@ -33,8 +32,8 @@ public class TreePanel extends JPanel { private JLabel status; - public TreePanel(LegupUI legupUI) { - this.legupUI = legupUI; + public TreePanel(/*LegupUI legupUI*/) { + //this.legupUI = legupUI; main = new JPanel(); diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeToolBarButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java similarity index 87% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeToolBarButton.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java index ab63bab81..85c1c73c8 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeToolBarButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import javax.swing.*; diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeToolBarName.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java similarity index 60% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeToolBarName.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java index 309fa3048..dd8a37c15 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeToolBarName.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; public enum TreeToolBarName { ADD_CHILD, DEL_CHILD, MERGE, COLLAPSE diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeToolbarPanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java similarity index 97% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeToolbarPanel.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java index 50b31d74c..97ea419e6 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeToolbarPanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import javax.swing.*; import java.awt.*; diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeTransitionView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java similarity index 99% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeTransitionView.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java index a71f92800..3f1f59ba5 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeTransitionView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.model.tree.TreeTransition; diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java similarity index 99% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeView.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java index 01ec07d32..79367216a 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.TreeController; @@ -21,8 +21,8 @@ import static edu.rpi.legup.model.tree.TreeElementType.NODE; import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; -import static edu.rpi.legup.ui.treeview.TreeNodeView.DIAMETER; -import static edu.rpi.legup.ui.treeview.TreeNodeView.RADIUS; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; public class TreeView extends ScrollView implements ITreeListener { private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); diff --git a/src/main/java/edu/rpi/legup/ui/treeview/TreeViewSelection.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java similarity index 98% rename from src/main/java/edu/rpi/legup/ui/treeview/TreeViewSelection.java rename to src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java index 0c76aa5e0..575d19f19 100644 --- a/src/main/java/edu/rpi/legup/ui/treeview/TreeViewSelection.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.ui.treeview; +package edu.rpi.legup.ui.proofeditorui.treeview; import java.awt.*; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java new file mode 100644 index 000000000..a7fe8bf7c --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java @@ -0,0 +1,23 @@ +package edu.rpi.legup.ui.puzzleeditorui.elementsview; + +import edu.rpi.legup.model.elements.Element; +import edu.rpi.legup.model.rules.Rule; + +import javax.swing.*; + +public class ElementButton extends JButton { + + private Element element; + ElementButton(Element e) { + super(e.getImageIcon()); + this.element = e; + } + + public Element getElement() { + return element; + } + + public void setElement(Element e) { + this.element = e; + } +} diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java new file mode 100644 index 000000000..ebb0e9ca4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java @@ -0,0 +1,73 @@ +package edu.rpi.legup.ui.puzzleeditorui.elementsview; + +import edu.rpi.legup.controller.EditorElementController; +import edu.rpi.legup.model.Puzzle; + +import javax.swing.*; +import javax.swing.border.TitledBorder; +import java.awt.*; + +public class ElementFrame extends JPanel { + private static final String checkBox = " \u2714 "; + private static final String xBox = " \u2718 "; + private static final String htmlHead = ""; + private static final String htmlTail = ""; + + private PlaceableElementPanel placeableElementPanel; + private NonPlaceableElementPanel nonPlaceableElementPanel; + private JTabbedPane tabbedPane; + private JLabel status; + private ButtonGroup buttonGroup; + + private EditorElementController controller; + + public ElementFrame(EditorElementController controller) { + this.controller = controller; + + this.tabbedPane = new JTabbedPane(); + this.status = new JLabel("", SwingConstants.CENTER); + this.buttonGroup = new ButtonGroup(); + + placeableElementPanel = new PlaceableElementPanel(this); + tabbedPane.addTab(placeableElementPanel.getName(), placeableElementPanel.getIcon(), new JScrollPane(placeableElementPanel), placeableElementPanel.getToolTip()); + + nonPlaceableElementPanel = new NonPlaceableElementPanel(this); + tabbedPane.addTab(nonPlaceableElementPanel.getName(), nonPlaceableElementPanel.getIcon(), new JScrollPane(nonPlaceableElementPanel), nonPlaceableElementPanel.getToolTip()); + + setLayout(new BorderLayout()); + setMinimumSize(new Dimension(250, 256)); + setPreferredSize(new Dimension(330, 256)); + + add(tabbedPane); + add(status, BorderLayout.SOUTH); + + TitledBorder title = BorderFactory.createTitledBorder("Elements"); + title.setTitleJustification(TitledBorder.CENTER); + setBorder(title); + } + + public ButtonGroup getButtonGroup() { + return buttonGroup; + } + + public void resetSize() { + int buttonWidth = ((ElementPanel) tabbedPane.getSelectedComponent()).getElementButtons()[0].getWidth(); + this.setMinimumSize(new Dimension(2 * buttonWidth + 64, this.getHeight())); + } + + public void setElements(Puzzle puzzle) { + placeableElementPanel.setElements(puzzle.getPlaceableElements()); + nonPlaceableElementPanel.setElements(puzzle.getNonPlaceableElements()); + } + + public EditorElementController getController() { + return controller; + } + + public JTabbedPane getTabbedPane() { + return tabbedPane; + } + + public NonPlaceableElementPanel getNonPlaceableElementPanel() { return nonPlaceableElementPanel; } + public PlaceableElementPanel getPlaceableElementPanel() { return placeableElementPanel; } +} diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java new file mode 100644 index 000000000..4c2bebd54 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java @@ -0,0 +1,82 @@ +package edu.rpi.legup.ui.puzzleeditorui.elementsview; + +import edu.rpi.legup.model.rules.Rule; +import edu.rpi.legup.ui.WrapLayout; +import edu.rpi.legup.model.elements.Element; +import edu.rpi.legup.ui.proofeditorui.rulesview.RuleButton; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public abstract class ElementPanel extends JPanel { + protected ImageIcon icon; + protected String name; + protected String toolTip; + protected ElementButton[] elementButtons; + protected ElementFrame elementFrame; + protected List elements; + + public ElementPanel(ElementFrame eFrame) { + this.elementFrame = eFrame; + this.elements = new ArrayList<>(); + setLayout(new WrapLayout()); + } + public void setElements(List elements) { + this.elements = elements; + clearButtons(); + + elementButtons = new ElementButton[elements.size()]; + + for (int i = 0; i < elements.size(); i++) { + Element element = elements.get(i); + elementButtons[i] = new ElementButton(element); + elementFrame.getButtonGroup().add(elementButtons[i]); + + elementButtons[i].setToolTipText(element.getElementName() + ": " + element.getDescription()); + elementButtons[i].addActionListener(elementFrame.getController()); + add(elementButtons[i]); + } + revalidate(); + } + + protected void clearButtons() { + if (elementButtons != null) { + removeAll(); + for (int x = 0; x < elementButtons.length; ++x) { + elementButtons[x].removeActionListener(elementFrame.getController()); + } + } + } + + public ElementButton[] getElementButtons() { + return elementButtons; + } + + public ImageIcon getIcon() { + return icon; + } + + public void setIcon(ImageIcon icon) { + this.icon = icon; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + public String getToolTip() { + return toolTip; + } + + public void setToolTip(String toolTip) { + this.toolTip = toolTip; + } +} + diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java new file mode 100644 index 000000000..6704d1aa2 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java @@ -0,0 +1,12 @@ +package edu.rpi.legup.ui.puzzleeditorui.elementsview; + +import javax.swing.*; + +public class NonPlaceableElementPanel extends ElementPanel{ + public NonPlaceableElementPanel(ElementFrame elementFrame) { + super(elementFrame); + this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Basic Rules.gif")); + this.name = "Non-Placeable Elements"; + this.toolTip = "Non-Placeable Elements"; + } +} diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java new file mode 100644 index 000000000..2021d43cc --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java @@ -0,0 +1,12 @@ +package edu.rpi.legup.ui.puzzleeditorui.elementsview; + +import javax.swing.*; + +public class PlaceableElementPanel extends ElementPanel{ + public PlaceableElementPanel(ElementFrame elementFrame) { + super(elementFrame); + this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Basic Rules.gif")); + this.name = "Placeable Elements"; + this.toolTip = "Placeable Elements"; + } +} diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java new file mode 100644 index 000000000..7cb7856e7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java @@ -0,0 +1,4 @@ +package edu.rpi.legup.ui.puzzleeditorui.resizeview; + +public class ResizePanel { +} diff --git a/src/main/resources/edu/rpi/legup/homepanel/edit.png b/src/main/resources/edu/rpi/legup/homepanel/edit.png new file mode 100644 index 000000000..af7cb9d4a Binary files /dev/null and b/src/main/resources/edu/rpi/legup/homepanel/edit.png differ diff --git a/src/main/resources/edu/rpi/legup/homepanel/newpuzzle.png b/src/main/resources/edu/rpi/legup/homepanel/newpuzzle.png new file mode 100644 index 000000000..24ebfd346 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/homepanel/newpuzzle.png differ diff --git a/src/main/resources/edu/rpi/legup/homepanel/openproof.png b/src/main/resources/edu/rpi/legup/homepanel/openproof.png new file mode 100644 index 000000000..69dbdabcc Binary files /dev/null and b/src/main/resources/edu/rpi/legup/homepanel/openproof.png differ diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index 3937fbc3b..5e685d554 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -1,14 +1,43 @@ - + - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java b/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java new file mode 100644 index 000000000..20e4e8966 --- /dev/null +++ b/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java @@ -0,0 +1,54 @@ +package puzzles.battleship.rules; + +import edu.rpi.legup.puzzle.battleship.BattleshipType; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import edu.rpi.legup.puzzle.battleship.Battleship; +import edu.rpi.legup.puzzle.battleship.BattleshipBoard; +import edu.rpi.legup.puzzle.battleship.BattleshipCell; +import edu.rpi.legup.puzzle.battleship.rules.AdjacentShipsContradictionRule; +import edu.rpi.legup.save.InvalidFileFormatException; + +import java.awt.*; + +public class AdjacentShipsContradictionRuleTest +{ + private static final AdjacentShipsContradictionRule RULE + = new AdjacentShipsContradictionRule(); + + private static Battleship battleship; + + @BeforeClass + public static void setUp() + { + MockGameBoardFacade.getInstance(); + battleship = new Battleship(); + } + + @Test + public void OrthogonalAdjacentTest() throws InvalidFileFormatException + { + TestUtilities.importTestBoard("puzzles/battleship/rules/" + + "AdjacentShipsContradictionRule/OrthogonalAdjacentBoards", + battleship); + TreeNode rootNode = battleship.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + BattleshipBoard board = (BattleshipBoard)transition.getBoard(); + BattleshipCell cell1 = board.getCell(1, 1); + BattleshipCell cell2 = board.getCell(1, 2); + BattleshipCell cell3 = board.getCell(2, 1); + BattleshipCell cell4 = board.getCell(2, 2); + + Assert.assertNull(RULE.checkContradiction( + (BattleshipBoard)transition.getBoard())); + + + } +}