From 240e25205c09631fd7d997615e010114979f0c4f Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Sat, 7 Jan 2023 13:09:00 -0500 Subject: [PATCH] Revert "Master Push for Fall 22" --- .github/workflows/publish-javadoc.yml | 21 - .gitignore | 51 +- README.md | 4 +- .../org.eclipse.jdt.ui/dialog_settings.xml | 11 + .../logback.1.6.0.20150526-2032.xml | 43 + .../org.eclipse.ui.ide/dialog_settings.xml | 11 + .../org.eclipse.ui.intro/dialog_settings.xml | 4 + .../dialog_settings.xml | 15 + .../org.eclipse.ui.workbench/workingsets.xml | 5 + .../org.eclipse.jdt.ui/dialog_settings.xml | 11 + .../logback.1.6.0.20150526-2032.xml | 43 + .../org.eclipse.ui.ide/dialog_settings.xml | 11 + .../dialog_settings.xml | 15 + .../org.eclipse.ui.workbench/workingsets.xml | 5 + bin/main/edu/rpi/legup/legup/config | 4 +- .../edu/rpi/legup/puzzle/skyscrapers/TODO.md | 11 + .../legup/puzzle/skyscrapers/rules/TODO.md | 34 +- build.gradle | 108 + build.gradle.kts | 90 - gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 0 .../bin/main/edu.rpi.legupupdate/VERSION | 1 + legup-update/build.gradle | 22 + .../java/edu/rpi/legupupdate/NetUtil.java | 112 + .../main/java/edu/rpi/legupupdate/Update.java | 252 ++ .../edu/rpi/legupupdate/UpdateProgress.java | 11 + .../resources/edu.rpi.legupupdate/VERSION | 1 + .../10x10 Fillapix Very Easy/03010000074 | 1 - puzzles files/lightup/10x10 Easy/1514228 | 1 - puzzles files/lightup/10x10 Easy/1721067 | 1 - puzzles files/lightup/10x10 Easy/5403393 | 1 - puzzles files/lightup/10x10 Easy/5774519 | 1 - puzzles files/lightup/10x10 Easy/954498 | 1 - puzzles files/lightup/10x10 Hard/5373598 | 1 - puzzles files/lightup/10x10 Hard/573404 | 1 - puzzles files/lightup/10x10 Hard/5787104 | 1 - puzzles files/lightup/10x10 Hard/8003166 | 1 - puzzles files/lightup/10x10 Hard/8918995 | 1 - puzzles files/lightup/10x10 Normal/2761230 | 1 - puzzles files/lightup/10x10 Normal/343176 | 1 - puzzles files/lightup/10x10 Normal/632466 | 1 - puzzles files/lightup/10x10 Normal/7752941 | 1 - puzzles files/lightup/10x10 Normal/8090631 | 1 - puzzles files/lightup/14x14 Easy/1412335 | 1 - puzzles files/lightup/14x14 Easy/1949915 | 1 - puzzles files/lightup/14x14 Easy/6778348 | 1 - puzzles files/lightup/14x14 Easy/976495 | 1 - puzzles files/lightup/14x14 Easy/9949966 | 1 - puzzles files/lightup/14x14 Hard/1974912 | 1 - puzzles files/lightup/14x14 Hard/3618696 | 1 - puzzles files/lightup/14x14 Hard/578987 | 1 - puzzles files/lightup/14x14 Hard/8087653 | 1 - puzzles files/lightup/14x14 Hard/9554192 | 1 - puzzles files/lightup/14x14 Normal/2659779 | 1 - puzzles files/lightup/14x14 Normal/448333 | 1 - puzzles files/lightup/14x14 Normal/5063453 | 1 - puzzles files/lightup/14x14 Normal/606275 | 1 - puzzles files/lightup/14x14 Normal/830231 | 1 - puzzles files/lightup/7x7 Easy/2408448 | 1 - puzzles files/lightup/7x7 Easy/2736784 | 1 - puzzles files/lightup/7x7 Easy/2855683 | 1 - puzzles files/lightup/7x7 Easy/3535701 | 1 - puzzles files/lightup/7x7 Easy/393454 | 1 - puzzles files/lightup/7x7 Easy/4238934 | 1 - puzzles files/lightup/7x7 Easy/4604288 | 1 - puzzles files/lightup/7x7 Easy/4608986 | 1 - puzzles files/lightup/7x7 Easy/517362 | 1 - puzzles files/lightup/7x7 Easy/5229613 | 1 - puzzles files/lightup/7x7 Easy/5435528 | 1 - puzzles files/lightup/7x7 Easy/5488697 | 1 - puzzles files/lightup/7x7 Easy/6110408 | 1 - puzzles files/lightup/7x7 Easy/6300100 | 1 - puzzles files/lightup/7x7 Easy/6506194 | 1 - puzzles files/lightup/7x7 Easy/69495 | 1 - puzzles files/lightup/7x7 Easy/7936304 | 1 - puzzles files/lightup/7x7 Easy/7982513 | 1 - puzzles files/lightup/7x7 Easy/8949430 | 1 - puzzles files/lightup/7x7 Easy/9080685 | 1 - puzzles files/lightup/7x7 Hard/1130370 | 1 - puzzles files/lightup/7x7 Hard/2790971 | 1 - puzzles files/lightup/7x7 Hard/3421347 | 1 - puzzles files/lightup/7x7 Hard/4159457 | 1 - puzzles files/lightup/7x7 Hard/4674087 | 1 - puzzles files/lightup/7x7 Hard/507817 | 1 - puzzles files/lightup/7x7 Hard/5280094 | 1 - puzzles files/lightup/7x7 Hard/5677803 | 1 - puzzles files/lightup/7x7 Hard/6178908 | 1 - puzzles files/lightup/7x7 Hard/8122162 | 1 - puzzles files/lightup/7x7 Normal/2637310 | 1 - puzzles files/lightup/7x7 Normal/2979943 | 1 - puzzles files/lightup/7x7 Normal/3710905 | 1 - puzzles files/lightup/7x7 Normal/3727425 | 1 - puzzles files/lightup/7x7 Normal/3787583 | 1 - puzzles files/lightup/7x7 Normal/5570754 | 1 - puzzles files/lightup/7x7 Normal/7270504 | 1 - puzzles files/lightup/7x7 Normal/8000000 | 1 - puzzles files/lightup/7x7 Normal/9489812 | 1 - puzzles files/lightup/7x7 Normal/9806740 | 1 - puzzles files/masyu/6x6 Masyu Easy/6E_b0011 | 1 - puzzles files/masyu/6x6 Masyu Easy/6E_b0012 | 1 - puzzles files/masyu/6x6 Masyu Easy/6E_b0013 | 1 - puzzles files/masyu/6x6 Masyu Easy/6E_b0014 | 1 - puzzles files/masyu/6x6 Masyu Easy/6E_b0015 | 1 - puzzles files/masyu/6x6 Masyu Hard/6H_b0011 | 1 - puzzles files/masyu/6x6 Masyu Hard/6H_b0012 | 1 - puzzles files/masyu/6x6 Masyu Hard/6H_b0013 | 1 - puzzles files/masyu/6x6 Masyu Hard/6H_b0014 | 1 - puzzles files/masyu/6x6 Masyu Hard/6H_b0015 | 1 - puzzles files/masyu/6x6 Masyu Medium/6M_b0011 | 1 - puzzles files/masyu/6x6 Masyu Medium/6M_b0012 | 1 - puzzles files/masyu/6x6 Masyu Medium/6M_b0013 | 1 - puzzles files/masyu/6x6 Masyu Medium/6M_b0014 | 1 - puzzles files/masyu/6x6 Masyu Medium/6M_b0015 | 1 - puzzles files/narukabe_export tet | 57 +- .../nurikabe/10x10 Nurikabe Hard/3323808 | 1 - .../nurikabe/10x10 Nurikabe Hard/7675382 | 1 - .../nurikabe/10x10 Nurikabe Hard/8264437 | 1 - .../nurikabe/10x10 Nurikabe Hard/8991218 | 1 - .../nurikabe/10x10 Nurikabe Hard/9120893 | 1 - .../nurikabe/10x10 Nurikabe Normal/2852229 | 1 - .../nurikabe/10x10 Nurikabe Normal/5759900 | 1 - .../nurikabe/10x10 Nurikabe Normal/6274292 | 1 - .../nurikabe/10x10 Nurikabe Normal/8920015 | 1 - .../nurikabe/10x10 Nurikabe Normal/9757357 | 1 - .../nurikabe/12x12 Nurikabe Hard/1276535 | 1 - .../nurikabe/12x12 Nurikabe Hard/4284912 | 1 - .../nurikabe/12x12 Nurikabe Hard/4459392 | 1 - .../nurikabe/12x12 Nurikabe Hard/7738199 | 1 - .../nurikabe/12x12 Nurikabe Hard/9205907 | 1 - .../nurikabe/12x12 Nurikabe Normal/1570101 | 1 - .../nurikabe/12x12 Nurikabe Normal/3731930 | 1 - .../nurikabe/12x12 Nurikabe Normal/3755957 | 1 - .../nurikabe/12x12 Nurikabe Normal/786806 | 1 - .../nurikabe/12x12 Nurikabe Normal/9946063 | 1 - .../nurikabe/15x15 Nurikabe Hard/1125631 | 1 - .../nurikabe/15x15 Nurikabe Hard/5955292 | 1 - .../nurikabe/15x15 Nurikabe Hard/6763936 | 1 - .../nurikabe/15x15 Nurikabe Hard/7005298 | 1 - .../nurikabe/15x15 Nurikabe Hard/9975093 | 1 - .../nurikabe/15x15 Nurikabe Normal/141420 | 1 - .../nurikabe/15x15 Nurikabe Normal/4123443 | 1 - .../nurikabe/15x15 Nurikabe Normal/731385 | 1 - .../nurikabe/15x15 Nurikabe Normal/8213677 | 1 - .../nurikabe/15x15 Nurikabe Normal/8372309 | 1 - .../nurikabe/20x20 Nurikabe Normal/175081 | 1 - .../nurikabe/20x20 Nurikabe Normal/3131243 | 1 - .../nurikabe/20x20 Nurikabe Normal/4400487 | 1 - .../nurikabe/20x20 Nurikabe Normal/5096090 | 1 - .../nurikabe/20x20 Nurikabe Normal/8595641 | 1 - .../nurikabe/5x5 Nurikabe Easy/118040 | 1 - .../nurikabe/5x5 Nurikabe Easy/235235 | 1 - .../nurikabe/5x5 Nurikabe Hard/118040 | 1 - .../nurikabe/5x5 Nurikabe Hard/1726232 | 1 - .../nurikabe/5x5 Nurikabe Hard/2168054 | 1 - .../nurikabe/5x5 Nurikabe Hard/7209030 | 1 - .../nurikabe/5x5 Nurikabe Hard/7897030 | 1 - .../nurikabe/5x5 Nurikabe Normal/1795638 | 1 - .../nurikabe/5x5 Nurikabe Normal/2663893 | 1 - .../nurikabe/5x5 Nurikabe Normal/3282122 | 1 - .../nurikabe/5x5 Nurikabe Normal/6403086 | 1 - .../nurikabe/5x5 Nurikabe Normal/7587733 | 1 - .../nurikabe/7x7 Nurikabe Hard/4478248 | 1 - .../nurikabe/7x7 Nurikabe Hard/5631322 | 1 - .../nurikabe/7x7 Nurikabe Hard/6734769 | 1 - .../nurikabe/7x7 Nurikabe Hard/8078467 | 1 - .../nurikabe/7x7 Nurikabe Hard/9225348 | 1 - .../nurikabe/7x7 Nurikabe Normal/42455 | 1 - .../nurikabe/7x7 Nurikabe Normal/4390561 | 1 - .../nurikabe/7x7 Nurikabe Normal/4877172 | 1 - .../nurikabe/7x7 Nurikabe Normal/7958242 | 1 - .../nurikabe/7x7 Nurikabe Normal/8786625 | 1 - puzzles files/shorttruthtable/DeMorgan.xml | 1 - puzzles files/shorttruthtable/Heuveln_01.xml | 1 - puzzles files/shorttruthtable/Heuveln_02.xml | 1 - puzzles files/shorttruthtable/Heuveln_03.xml | 1 - puzzles files/shorttruthtable/Heuveln_04.xml | 1 - .../shorttruthtable/Heuveln_04.xml_test02 | 47 +- .../Heuveln_04_export test.xml | 1 - .../shorttruthtable/Heuveln_04_test.xml | 1 - .../shorttruthtable/Heuveln_04_test01.xml | 1 - .../shorttruthtable/Heuveln_04_test03.xml | 1 - .../shorttruthtable/Heuveln_04_test_exprt.xml | 1 - puzzles files/shorttruthtable/Heuveln_05.xml | 1 - puzzles files/shorttruthtable/invalid1.xml | 1 - puzzles files/shorttruthtable/invalid2.xml | 1 - puzzles files/shorttruthtable/invalid3.xml | 1 - puzzles files/shorttruthtable/test.xml | 1 - puzzles files/skyscrapers/1646651 | 1 - .../skyscrapers/4x4 Skyscrapers Easy1 | 24 - .../skyscrapers/4x4 Skyscrapers Easy3 | 23 - .../skyscrapers/5x5 Skyscrapers Easy1 | 26 - .../skyscrapers/5x5 Skyscrapers Easy2 | 26 - .../skyscrapers/5x5 Skyscrapers Easy3 | 26 - .../skyscrapers/5x5 Skyscrapers Medium1 | 26 - .../skyscrapers/5x5 Skyscrapers Medium2 | 26 - .../skyscrapers/5x5 Skyscrapers Medium3 | 26 - .../{4x4 Skyscrapers Easy2 => easy} | 11 +- puzzles files/skyscrapers/easy1.xml | 1 - .../sudoku/3x3 Sudoku Advanced/15189327 | 1 - .../sudoku/3x3 Sudoku Advanced/20628823 | 1 - .../sudoku/3x3 Sudoku Advanced/45527956 | 1 - .../sudoku/3x3 Sudoku Advanced/9370592 | 1 - .../sudoku/3x3 Sudoku Advanced/9680726 | 1 - .../sudoku/3x3 Sudoku Basic/14538464 | 1 - .../sudoku/3x3 Sudoku Basic/32722678 | 1 - .../sudoku/3x3 Sudoku Basic/61362355 | 1 - .../sudoku/3x3 Sudoku Basic/63397584 | 1 - .../sudoku/3x3 Sudoku Basic/75565282 | 1 - puzzles files/sudoku/3x3 Sudoku Easy/1602661 | 1 - puzzles files/sudoku/3x3 Sudoku Easy/17756306 | 1 - puzzles files/sudoku/3x3 Sudoku Easy/48718527 | 1 - puzzles files/sudoku/3x3 Sudoku Easy/50638522 | 1 - puzzles files/sudoku/3x3 Sudoku Easy/52671552 | 1 - puzzles files/sudoku/3x3 Sudoku Evil/10251277 | 1 - puzzles files/sudoku/3x3 Sudoku Evil/112402 | 1 - puzzles files/sudoku/3x3 Sudoku Evil/285907 | 1 - puzzles files/sudoku/3x3 Sudoku Evil/3152911 | 1 - puzzles files/sudoku/3x3 Sudoku Evil/89765 | 1 - .../sudoku/3x3 Sudoku Extreme/10119634 | 1 - .../sudoku/3x3 Sudoku Extreme/1922600 | 1 - .../sudoku/3x3 Sudoku Extreme/3924003 | 1 - .../sudoku/3x3 Sudoku Extreme/469747 | 1 - .../sudoku/3x3 Sudoku Extreme/7643647 | 1 - .../sudoku/3x3 Sudoku Intermediate/22925923 | 1 - .../sudoku/3x3 Sudoku Intermediate/44179211 | 1 - .../sudoku/3x3 Sudoku Intermediate/59467143 | 1 - .../sudoku/3x3 Sudoku Intermediate/7405304 | 1 - .../sudoku/3x3 Sudoku Intermediate/78017020 | 1 - .../treetent/12x12 TreeTent Easy/061177 | 1 - .../treetent/12x12 TreeTent Easy/198217 | 1 - .../treetent/12x12 TreeTent Easy/255410 | 1 - .../treetent/12x12 TreeTent Easy/790635 | 1 - .../treetent/12x12 TreeTent Easy/805457 | 1 - .../treetent/12x12 TreeTent Medium/154674 | 1 - .../treetent/12x12 TreeTent Medium/330689 | 1 - .../treetent/12x12 TreeTent Medium/538746 | 1 - .../treetent/12x12 TreeTent Medium/579921 | 1 - .../treetent/12x12 TreeTent Medium/723664 | 1 - .../treetent/8x8 TreeTent Easy/13547135 | 1 - .../treetent/8x8 TreeTent Easy/1646651 | 1 - .../treetent/8x8 TreeTent Easy/1789167 | 1 - .../treetent/8x8 TreeTent Easy/32518510 | 1 - .../treetent/8x8 TreeTent Easy/4614656 | 1 - .../treetent/8x8 TreeTent Medium/351654 | 1 - .../treetent/8x8 TreeTent Medium/35496 | 1 - .../treetent/8x8 TreeTent Medium/4656816 | 1 - .../treetent/8x8 TreeTent Medium/6549871 | 1 - .../treetent/8x8 TreeTent Medium/989496 | 1 - settings.gradle | 4 + settings.gradle.kts | 2 - src/.idea/.gitignore | 3 + .../org_junit_jupiter_junit_jupiter_5_4_2.xml | 17 + src/.idea/misc.xml | 9 + src/.idea/modules.xml | 8 + src/.idea/src.iml | 12 + src/.idea/vcs.xml | 6 + src/main/java/edu/.idea/.gitignore | 3 + src/main/java/edu/.idea/edu.iml | 9 + src/main/java/edu/.idea/misc.xml | 6 + src/main/java/edu/.idea/modules.xml | 8 + src/main/java/edu/.idea/vcs.xml | 6 + src/main/java/edu/build.gradle | 62 + src/main/java/edu/rpi/.metadata/.lock | 0 .../.mylyn/.taskListIndex/segments.gen | Bin 0 -> 20 bytes .../.mylyn/.taskListIndex/segments_1 | Bin 0 -> 32 bytes .../.root/.indexes/history.version | 1 + .../.root/.indexes/properties.index | Bin 0 -> 57 bytes .../.root/.indexes/properties.version | 1 + .../org.eclipse.core.resources/.root/2.tree | Bin 0 -> 81 bytes .../.safetable/org.eclipse.core.resources | Bin 0 -> 436 bytes .../org.eclipse.core.resources.prefs | 2 + .../.settings/org.eclipse.jdt.ui.prefs | 13 + .../.settings/org.eclipse.m2e.discovery.prefs | 2 + .../org.eclipse.mylyn.context.core.prefs | 2 + .../org.eclipse.mylyn.monitor.ui.prefs | 2 + .../org.eclipse.mylyn.tasks.ui.prefs | 5 + .../.settings/org.eclipse.ui.ide.prefs | 5 + .../.settings/org.eclipse.ui.prefs | 2 + .../.settings/org.eclipse.ui.workbench.prefs | 3 + .../org.eclipse.e4.workbench/workbench.xmi | 2304 ++++++++++++++++ .../history/_0.fdt | Bin 0 -> 11 bytes .../history/_0.fdx | Bin 0 -> 12 bytes .../history/_0.fnm | 1 + .../history/_0.frq | 0 .../history/_0.nrm | 1 + .../history/_0.tii | Bin 0 -> 24 bytes .../history/_0.tis | Bin 0 -> 24 bytes .../history/segments.gen | Bin 0 -> 20 bytes .../history/segments_1 | Bin 0 -> 243 bytes .../remote-index/_0.fdt | Bin 0 -> 11 bytes .../remote-index/_0.fdx | Bin 0 -> 12 bytes .../remote-index/_0.fnm | 1 + .../remote-index/_0.frq | 0 .../remote-index/_0.nrm | 1 + .../remote-index/_0.tii | Bin 0 -> 24 bytes .../remote-index/_0.tis | Bin 0 -> 24 bytes .../remote-index/segments.gen | Bin 0 -> 20 bytes .../remote-index/segments_1 | Bin 0 -> 243 bytes .../assumedExternalFilesCache | Bin 0 -> 4 bytes .../org.eclipse.jdt.core/externalFilesCache | Bin 0 -> 4 bytes .../org.eclipse.jdt.core/nonChainingJarsCache | Bin 0 -> 4 bytes .../variablesAndContainers.dat | Bin 0 -> 110 bytes .../org.eclipse.jdt.ui/OpenTypeHistory.xml | 2 + .../QualifiedTypeNameHistory.xml | 2 + .../org.eclipse.jdt.ui/dialog_settings.xml | 11 + .../logback.1.6.0.20150526-2032.xml | 43 + .../org.eclipse.oomph.setup/workspace.setup | 6 + .../org.eclipse.ui.ide/dialog_settings.xml | 11 + .../org.eclipse.ui.intro/dialog_settings.xml | 4 + .../dialog_settings.xml | 15 + .../org.eclipse.ui.workbench/workingsets.xml | 5 + src/main/java/edu/rpi/.metadata/version.ini | 3 + src/main/java/edu/rpi/legup/.metadata/.lock | 0 .../.mylyn/.taskListIndex/segments.gen | Bin 0 -> 20 bytes .../.mylyn/.taskListIndex/segments_1 | Bin 0 -> 32 bytes .../.root/.indexes/history.version | 1 + .../.root/.indexes/properties.index | Bin 0 -> 57 bytes .../.root/.indexes/properties.version | 1 + .../org.eclipse.core.resources/.root/2.tree | Bin 0 -> 81 bytes .../.safetable/org.eclipse.core.resources | Bin 0 -> 436 bytes .../org.eclipse.core.resources.prefs | 2 + .../.settings/org.eclipse.jdt.ui.prefs | 13 + .../.settings/org.eclipse.m2e.discovery.prefs | 2 + .../org.eclipse.mylyn.context.core.prefs | 2 + .../org.eclipse.mylyn.monitor.ui.prefs | 2 + .../org.eclipse.mylyn.tasks.ui.prefs | 5 + .../.settings/org.eclipse.ui.ide.prefs | 5 + .../.settings/org.eclipse.ui.prefs | 2 + .../.settings/org.eclipse.ui.workbench.prefs | 3 + .../org.eclipse.e4.workbench/workbench.xmi | 2308 +++++++++++++++++ .../history/_0.fdt | Bin 0 -> 11 bytes .../history/_0.fdx | Bin 0 -> 12 bytes .../history/_0.fnm | 1 + .../history/_0.frq | 0 .../history/_0.nrm | 1 + .../history/_0.tii | Bin 0 -> 24 bytes .../history/_0.tis | Bin 0 -> 24 bytes .../history/segments.gen | Bin 0 -> 20 bytes .../history/segments_1 | Bin 0 -> 243 bytes .../remote-index/_0.fdt | Bin 0 -> 11 bytes .../remote-index/_0.fdx | Bin 0 -> 12 bytes .../remote-index/_0.fnm | 1 + .../remote-index/_0.frq | 0 .../remote-index/_0.nrm | 1 + .../remote-index/_0.tii | Bin 0 -> 24 bytes .../remote-index/_0.tis | Bin 0 -> 24 bytes .../remote-index/segments.gen | Bin 0 -> 20 bytes .../remote-index/segments_1 | Bin 0 -> 243 bytes .../assumedExternalFilesCache | Bin 0 -> 4 bytes .../org.eclipse.jdt.core/externalFilesCache | Bin 0 -> 4 bytes .../org.eclipse.jdt.core/nonChainingJarsCache | Bin 0 -> 4 bytes .../variablesAndContainers.dat | Bin 0 -> 110 bytes .../org.eclipse.jdt.ui/OpenTypeHistory.xml | 2 + .../QualifiedTypeNameHistory.xml | 2 + .../org.eclipse.jdt.ui/dialog_settings.xml | 11 + .../logback.1.6.0.20150526-2032.xml | 43 + .../org.eclipse.oomph.setup/workspace.setup | 6 + .../org.eclipse.ui.ide/dialog_settings.xml | 11 + .../dialog_settings.xml | 15 + .../org.eclipse.ui.workbench/workingsets.xml | 5 + .../java/edu/rpi/legup/.metadata/version.ini | 3 + .../controller/EditorElementController.java | 18 - .../legup/history/AutoCaseRuleCommand.java | 4 - .../lightup/rules/MustLightBasicRule.java | 85 +- .../edu/rpi/legup/puzzle/masyu/MasyuCell.java | 2 - .../legup/puzzle/masyu/MasyuController.java | 16 +- .../edu/rpi/legup/puzzle/masyu/MasyuType.java | 2 +- .../puzzle/nurikabe/NurikabeUtilities.java | 122 +- .../rules/BlackBottleNeckBasicRule.java | 2 +- .../rules/CannotReachCellBasicRule.java | 12 +- ...UnreachableWhiteCellContradictionRule.java | 63 +- .../legup/puzzle/skyscrapers/Skyscrapers.java | 4 + .../puzzle/skyscrapers/SkyscrapersBoard.java | 198 +- .../skyscrapers/SkyscrapersCellFactory.java | 11 +- .../puzzle/skyscrapers/SkyscrapersClue.java | 6 +- .../skyscrapers/SkyscrapersClueView.java | 8 +- .../skyscrapers/SkyscrapersController.java | 12 +- .../skyscrapers/SkyscrapersElementView.java | 25 +- .../skyscrapers/SkyscrapersExporter.java | 8 +- .../skyscrapers/SkyscrapersImporter.java | 94 +- .../skyscrapers/SkyscrapersLineView.java | 1 + .../puzzle/skyscrapers/SkyscrapersType.java | 2 +- .../puzzle/skyscrapers/SkyscrapersView.java | 98 +- .../edu/rpi/legup/puzzle/skyscrapers/TODO.md | 11 + .../rules/CellForNumberCaseRule.java | 135 - .../DuplicateNumberContradictionRule.java | 5 +- .../ExceedingVisibilityContradictionRule.java | 178 +- ...rBasicRule.java => FixedMaxBasicRule.java} | 61 +- ...sufficientVisibilityContradictionRule.java | 175 +- .../skyscrapers/rules/LastCellBasicRule.java | 132 + ...asicRule.java => LastNumberBasicRule.java} | 77 +- .../rules/LastVisibleCellBasicRule.java | 117 - .../skyscrapers/rules/NEdgeBasicRule.java | 10 +- ...erBasicRule.java => OneEdgeBasicRule.java} | 64 +- ...ule.java => PossibleContentsCaseRule.java} | 54 +- ...PreemptiveVisibilityContradictionRule.java | 193 -- .../skyscrapers/rules/Skyscrapers Ruleset.pdf | Bin 194684 -> 0 bytes .../legup/puzzle/skyscrapers/rules/TODO.md | 34 +- .../UnresolvedCellContradictionRule.java | 34 +- .../UnresolvedNumberContradictionRule.java | 97 - .../rules/skyscrapers_reference_sheet.txt | 59 +- src/main/java/edu/rpi/legup/ui/HomePanel.java | 484 +--- src/main/java/edu/rpi/legup/ui/LegupUI.java | 2 + .../edu/rpi/legup/ui/ProofEditorPanel.java | 183 +- .../edu/rpi/legup/ui/PuzzleEditorPanel.java | 41 +- .../proofeditorui/rulesview/RuleButton.java | 2 +- .../ui/proofeditorui/rulesview/RuleFrame.java | 19 +- .../ui/proofeditorui/rulesview/RulePanel.java | 186 +- .../rulesview/SearchBarPanel.java | 19 - .../elementsview/ElementButton.java | 14 - .../elementsview/ElementFrame.java | 13 - .../java/edu/rpi/legup/user/Submission.java | 13 + .../edu/rpi/legup/user/UsageStatistics.java | 63 + src/main/java/edu/rpi/legup/user/User.java | 5 + .../images/skyscraper/DuplicateNumber.png | Bin 0 -> 2413 bytes .../legup/images/skyscraper/LastNumber.png | Bin 0 -> 28799 bytes .../images/skyscraper/PossibleContents.png | Bin 0 -> 67322 bytes .../skyscrapers/cases/CellForNumber.png | Bin 1871 -> 0 bytes .../skyscrapers/cases/NumberForCell.png | Bin 1875 -> 0 bytes .../contradictions/DuplicateNumber.png | Bin 954 -> 0 bytes .../contradictions/ExceedingVisibility.png | Bin 1184 -> 0 bytes .../contradictions/InsufficientVisibility.png | Bin 1291 -> 0 bytes .../contradictions/PreemptiveVisibility.png | Bin 1101 -> 0 bytes .../contradictions/UnresolvedCell.png | Bin 1165 -> 0 bytes .../contradictions/UnresolvedNumber.png | Bin 1357 -> 0 bytes .../images/skyscrapers/rules/FixedMax.png | Bin 1225 -> 0 bytes .../images/skyscrapers/rules/LastCell.png | Bin 1352 -> 0 bytes .../images/skyscrapers/rules/LastNumber.png | Bin 1335 -> 0 bytes .../legup/images/skyscrapers/rules/NEdge.png | Bin 1523 -> 0 bytes .../images/skyscrapers/rules/OneEdge.png | Bin 934 -> 0 bytes 430 files changed, 6812 insertions(+), 2938 deletions(-) delete mode 100644 .github/workflows/publish-javadoc.yml create mode 100644 bin/main/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml create mode 100644 bin/main/edu/rpi/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml create mode 100644 bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml create mode 100644 bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml create mode 100644 bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml create mode 100644 bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml create mode 100644 bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml create mode 100644 bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml create mode 100644 bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml create mode 100644 bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml create mode 100644 bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml create mode 100644 bin/main/edu/rpi/legup/puzzle/skyscrapers/TODO.md create mode 100644 build.gradle delete mode 100644 build.gradle.kts mode change 100755 => 100644 gradlew create mode 100644 legup-update/bin/main/edu.rpi.legupupdate/VERSION create mode 100644 legup-update/build.gradle create mode 100644 legup-update/src/main/java/edu/rpi/legupupdate/NetUtil.java create mode 100644 legup-update/src/main/java/edu/rpi/legupupdate/Update.java create mode 100644 legup-update/src/main/java/edu/rpi/legupupdate/UpdateProgress.java create mode 100644 legup-update/src/main/resources/edu.rpi.legupupdate/VERSION delete mode 100644 puzzles files/skyscrapers/4x4 Skyscrapers Easy1 delete mode 100644 puzzles files/skyscrapers/4x4 Skyscrapers Easy3 delete mode 100644 puzzles files/skyscrapers/5x5 Skyscrapers Easy1 delete mode 100644 puzzles files/skyscrapers/5x5 Skyscrapers Easy2 delete mode 100644 puzzles files/skyscrapers/5x5 Skyscrapers Easy3 delete mode 100644 puzzles files/skyscrapers/5x5 Skyscrapers Medium1 delete mode 100644 puzzles files/skyscrapers/5x5 Skyscrapers Medium2 delete mode 100644 puzzles files/skyscrapers/5x5 Skyscrapers Medium3 rename puzzles files/skyscrapers/{4x4 Skyscrapers Easy2 => easy} (77%) create mode 100644 settings.gradle delete mode 100644 settings.gradle.kts create mode 100644 src/.idea/.gitignore create mode 100644 src/.idea/libraries/org_junit_jupiter_junit_jupiter_5_4_2.xml create mode 100644 src/.idea/misc.xml create mode 100644 src/.idea/modules.xml create mode 100644 src/.idea/src.iml create mode 100644 src/.idea/vcs.xml create mode 100644 src/main/java/edu/.idea/.gitignore create mode 100644 src/main/java/edu/.idea/edu.iml create mode 100644 src/main/java/edu/.idea/misc.xml create mode 100644 src/main/java/edu/.idea/modules.xml create mode 100644 src/main/java/edu/.idea/vcs.xml create mode 100644 src/main/java/edu/build.gradle create mode 100644 src/main/java/edu/rpi/.metadata/.lock create mode 100644 src/main/java/edu/rpi/.metadata/.mylyn/.taskListIndex/segments.gen create mode 100644 src/main/java/edu/rpi/.metadata/.mylyn/.taskListIndex/segments_1 create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.resources/.root/2.tree create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdt create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdx create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fnm create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.frq create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.nrm create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tii create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tis create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments.gen create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments_1 create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdt create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdx create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fnm create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.frq create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.nrm create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tii create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tis create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments.gen create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments_1 create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml create mode 100644 src/main/java/edu/rpi/.metadata/version.ini create mode 100644 src/main/java/edu/rpi/legup/.metadata/.lock create mode 100644 src/main/java/edu/rpi/legup/.metadata/.mylyn/.taskListIndex/segments.gen create mode 100644 src/main/java/edu/rpi/legup/.metadata/.mylyn/.taskListIndex/segments_1 create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.root/2.tree create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdt create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdx create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fnm create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.frq create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.nrm create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tii create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tis create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments.gen create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments_1 create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdt create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdx create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fnm create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.frq create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.nrm create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tii create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tis create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments.gen create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments_1 create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml create mode 100644 src/main/java/edu/rpi/legup/.metadata/version.ini create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/TODO.md delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java rename src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/{LastSingularNumberBasicRule.java => FixedMaxBasicRule.java} (63%) create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastCellBasicRule.java rename src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/{LastSingularCellBasicRule.java => LastNumberBasicRule.java} (58%) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellBasicRule.java rename src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/{LastVisibleNumberBasicRule.java => OneEdgeBasicRule.java} (63%) rename src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/{NumberForCellCaseRule.java => PossibleContentsCaseRule.java} (75%) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/Skyscrapers Ruleset.pdf delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java create mode 100644 src/main/java/edu/rpi/legup/user/Submission.java create mode 100644 src/main/java/edu/rpi/legup/user/UsageStatistics.java create mode 100644 src/main/java/edu/rpi/legup/user/User.java create mode 100644 src/main/resources/edu/rpi/legup/images/skyscraper/DuplicateNumber.png create mode 100644 src/main/resources/edu/rpi/legup/images/skyscraper/LastNumber.png create mode 100644 src/main/resources/edu/rpi/legup/images/skyscraper/PossibleContents.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/rules/FixedMax.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png delete mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png diff --git a/.github/workflows/publish-javadoc.yml b/.github/workflows/publish-javadoc.yml deleted file mode 100644 index 99183fdee..000000000 --- a/.github/workflows/publish-javadoc.yml +++ /dev/null @@ -1,21 +0,0 @@ -# modified from https://github.com/MathieuSoysal/Javadoc-publisher.yml - -name: Publish Javadoc - -on: - push: - branches: - - dev - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Publish JavaDoc - uses: MathieuSoysal/Javadoc-publisher.yml@v2.3.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - javadoc-branch: javadoc - java-version: 8 - target-folder: docs - project: gradle diff --git a/.gitignore b/.gitignore index baee37e51..a09e801d8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,38 @@ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + # CMake cmake-build-*/ +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + # File-based project format *.iws @@ -23,19 +52,32 @@ native/windows/bin/* # JIRA plugin atlassian-ide-plugin.xml +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + ### Intellij+all Patch ### # Ignore everything but code style settings and run configurations # that are supposed to be shared within teams. -**/.idea -!.idea/codeStyles/* -!.idea/runConfigurations/* +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations ### Java ### # Compiled class file @@ -90,6 +132,3 @@ gradle-app.setting **/.DS_Store gradle/wrapper/gradle-wrapper.properties - -# Visual Studio Code configs -.vscode/* \ No newline at end of file diff --git a/README.md b/README.md index ea9df1770..c59342248 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

GPL 3.0 License @@ -47,8 +47,6 @@ Additionally, if you are interested in computer science and programming, please ## Documentation Documentation is actively being worked on on the [Legup wiki](https://github.com/Bram-Hub/Legup/wiki). -The Javadocs for our application are currently hosted directly on [our Github Pages site](https://bram-hub.github.io/Legup/). - Documentation is very much in the early stages, and we would greatly appreciate anyone who is willing to help write and structure the documentation. Currently, the priority is to write detailed documentation on how Nurikabe works, as it is the puzzle that is the most developed within Legup. ## Contributing diff --git a/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml new file mode 100644 index 000000000..105ea7e2b --- /dev/null +++ b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -0,0 +1,11 @@ + +

+
+ + + + + +
+
diff --git a/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml new file mode 100644 index 000000000..63d411340 --- /dev/null +++ b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml @@ -0,0 +1,43 @@ + + + + %date [%thread] %-5level %logger{35} - %msg%n + + + OFF + + + + + ${org.eclipse.m2e.log.dir}/0.log + + ${org.eclipse.m2e.log.dir}/%i.log + 1 + 10 + + + 100MB + + + %date [%thread] %-5level %logger{35} - %msg%n + + + + + + WARN + + + + + + + + + + + + + + + diff --git a/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml new file mode 100644 index 000000000..c00d6ca9e --- /dev/null +++ b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml @@ -0,0 +1,11 @@ + +
+
+ + +
+
+ + +
+
diff --git a/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml new file mode 100644 index 000000000..541da6a75 --- /dev/null +++ b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml @@ -0,0 +1,4 @@ + +
+ +
diff --git a/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml new file mode 100644 index 000000000..01f7412af --- /dev/null +++ b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml @@ -0,0 +1,15 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml new file mode 100644 index 000000000..ef53d8bf2 --- /dev/null +++ b/bin/main/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml new file mode 100644 index 000000000..0bf6a8c76 --- /dev/null +++ b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -0,0 +1,11 @@ + +
+
+ + + + + +
+
diff --git a/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml new file mode 100644 index 000000000..63d411340 --- /dev/null +++ b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml @@ -0,0 +1,43 @@ + + + + %date [%thread] %-5level %logger{35} - %msg%n + + + OFF + + + + + ${org.eclipse.m2e.log.dir}/0.log + + ${org.eclipse.m2e.log.dir}/%i.log + 1 + 10 + + + 100MB + + + %date [%thread] %-5level %logger{35} - %msg%n + + + + + + WARN + + + + + + + + + + + + + + + diff --git a/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml new file mode 100644 index 000000000..c00d6ca9e --- /dev/null +++ b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml @@ -0,0 +1,11 @@ + +
+
+ + +
+
+ + +
+
diff --git a/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml new file mode 100644 index 000000000..d2ff1eed0 --- /dev/null +++ b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml @@ -0,0 +1,15 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml new file mode 100644 index 000000000..5e61d80ea --- /dev/null +++ b/bin/main/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index 8e3b4b84c..cfa1ebeac 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -15,7 +15,7 @@ + fileCreationDisabled="false"/> + fileCreationDisabled="false"/> ("buildNativeWindows") { - dependsOn(shadowJar, createExe) - workingDir = File("${project.buildDir}/../native/windows") - commandLine("cmd", "/c", "make_windows_installer.bat") - } -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2d2fd7c74..830458866 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 diff --git a/legup-update/bin/main/edu.rpi.legupupdate/VERSION b/legup-update/bin/main/edu.rpi.legupupdate/VERSION new file mode 100644 index 000000000..227cea215 --- /dev/null +++ b/legup-update/bin/main/edu.rpi.legupupdate/VERSION @@ -0,0 +1 @@ +2.0.0 diff --git a/legup-update/build.gradle b/legup-update/build.gradle new file mode 100644 index 000000000..097f18413 --- /dev/null +++ b/legup-update/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'java' +} + +version '2.0' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile 'com.google.code.gson:gson:2.8.2' + compile 'commons-cli:commons-cli:1.4' + compile 'commons-io:commons-io:2.6' + compile 'org.apache.commons:commons-lang3:3.7' + compile 'org.apache.logging.log4j:log4j-api:2.10.0' + compile 'org.apache.logging.log4j:log4j-core:2.10.0' + + testCompile group: 'junit', name: 'junit', version: '4.12' +} \ No newline at end of file diff --git a/legup-update/src/main/java/edu/rpi/legupupdate/NetUtil.java b/legup-update/src/main/java/edu/rpi/legupupdate/NetUtil.java new file mode 100644 index 000000000..03eb5e38d --- /dev/null +++ b/legup-update/src/main/java/edu/rpi/legupupdate/NetUtil.java @@ -0,0 +1,112 @@ +package edu.rpi.legupupdate; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class NetUtil { + + public static final String ARIS_NAME = "Aris-Java"; + public static final String AUTH_BAN = "AUTH BAN"; + public static final String AUTH_FAIL = "AUTH FAIL"; + public static final String AUTH_OK = "AUTH OK"; + public static final String AUTH_ERR = "AUTH UNKNOWN_ERROR"; + public static final String AUTH_INVALID = "AUTH INVALID"; + public static final String AUTH = "AUTH"; + public static final String AUTH_PASS = "PASS"; + public static final String AUTH_ACCESS_TOKEN = "TOKEN"; + + public static final String USER_STUDENT = "student"; + public static final String USER_INSTRUCTOR = "instructor"; + + public static final long MAX_FILE_SIZE = 5242880; // 5MiB + public static final String UNAUTHORIZED = "UNAUTHORIZED"; + public static final String ERROR = "UNKNOWN_ERROR"; + public static final String OK = "OK"; + public static final String INVALID = "INVALID"; + + public static final String INVALID_VERSION = "INVALID VERSION"; + public static final String VERSION_OK = "VERSION OK"; + public static final String DONE = "DONE"; + public static final String USER_EXISTS = "EXISTS"; + + public static final String STATUS_GRADING = "Grading"; + public static final String STATUS_CORRECT = "Correct"; + public static final String STATUS_INCORRECT = "Incorrect"; + public static final String STATUS_ERROR = "Grading Error. Please Resubmit"; + public static final String STATUS_NO_SUBMISSION = "No Submissions"; + + public static final int SOCKET_TIMEOUT = 15000; + public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + public static final DateTimeFormatter ZDT_FORMAT = DateTimeFormatter.ISO_DATE_TIME; + public static final String TOO_LARGE = "TOO_LARGE"; + public static final String NO_DATA = "NO_DATA"; + public static final String RENAME = "RENAME"; + public static final String ADD_PROOF = "ADD_PROOF"; + public static final String REMOVE_PROOF = "REMOVE_PROOF"; + public static final String CHANGE_DUE = "CHANGE_DUE"; + public static final String UPLOAD = "UPLOAD"; + + /** + * Compares two version strings. + *

+ * Use this instead of String.compareTo() for a non-lexicographical + * comparison that works for version strings. e.g. "1.10".compareTo("1.6"). + *

+ *

+ *

+ * Note: It does not work if "1.10" is supposed to be equal to "1.10.0". + * + * @param str1 a string of ordinal numbers separated by decimal points. + * @param str2 a string of ordinal numbers separated by decimal points. + * @return The result is a negative integer if str1 is numerically less than str2. + * The result is a positive integer if str1 is numerically greater than str2. + * The result is zero if the strings are numerically equal. + */ + public static int versionCompare(String str1, String str2) { + String[] vals1 = str1.split("\\."); + String[] vals2 = str2.split("\\."); + int i = 0; + // set index to first non-equal ordinal or length of shortest version string + while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) { + i++; + } + // compare first non-equal ordinal number + if (i < vals1.length && i < vals2.length) { + int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])); + return Integer.signum(diff); + } + // the strings are equal or one string is a substring of the other + // e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4" + return Integer.signum(vals1.length - vals2.length); + } + + public static ZonedDateTime localToUTC(LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC); + } + + public static ZonedDateTime zoneToUTC(ZonedDateTime dateTime) { + return dateTime.withZoneSameInstant(ZoneOffset.UTC); + } + + public static LocalDateTime UTCToLocal(ZonedDateTime utcTime) { + return utcTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime(); + } + + public static Timestamp ZDTToTimestamp(ZonedDateTime zdt) { + return new Timestamp(UTCToMilli(zoneToUTC(zdt))); + } + + public static ZonedDateTime zoneToLocal(ZonedDateTime dateTime) { + return dateTime.withZoneSameInstant(ZoneOffset.systemDefault()); + } + + public static long UTCToMilli(ZonedDateTime utcTime) { + return zoneToLocal(utcTime).toInstant().toEpochMilli(); + } + +} diff --git a/legup-update/src/main/java/edu/rpi/legupupdate/Update.java b/legup-update/src/main/java/edu/rpi/legupupdate/Update.java new file mode 100644 index 000000000..578c4f002 --- /dev/null +++ b/legup-update/src/main/java/edu/rpi/legupupdate/Update.java @@ -0,0 +1,252 @@ +package edu.rpi.legupupdate; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static edu.rpi.legupupdate.Update.Stream.CLIENT; + +public class Update { + + private static final Logger logger = Logger.getLogger(Update.class.getName()); + + private static final String RELEASE_CHECK_URL = "https://api.github.com/repos/jpoegs/Legup2.0/releases/latest"; + private static final String REPO_BASE_URL = "https://github.com/jpoegs/Legup2.0/releases/download/"; + private static final String MAVEN_BASE_URL = "http://central.maven.org/maven2/"; + private static final String CLIENT_LIBS_LOC = "/client/client.iml"; + private static final String LIBRARY_LINE_ID = "type=\"library\""; + private static final Pattern LIB_PATTERN = Pattern.compile("(?<=name=\").*?(?=\")"); + private static final int UPDATE_EXIT_CODE = 52; + private File downloadDir; + private Stream updateStream; + private JsonObject releaseData; + private String updateVersion; + private UpdateProgress progress; + + public static final String VERSION; + + static { + BufferedReader reader = new BufferedReader(new InputStreamReader(Update.class.getResourceAsStream("/edu/rpi/legup/VERSION"))); + String version = "UNKNOWN"; + try { + version = reader.readLine(); + reader.close(); + } + catch (IOException e) { + logger.severe("An error occurred while attempting to read the version\n" + e.getMessage()); + } + VERSION = version; + } + + public Update(Stream updateStream, File updateDownloadDir) { + Objects.requireNonNull(updateStream); + Objects.requireNonNull(updateDownloadDir); + this.downloadDir = updateDownloadDir; + this.updateStream = updateStream; + } + + public void setUpdateProgress(UpdateProgress progress) { + this.progress = progress; + } + + public boolean checkUpdate() { + try { + if (progress != null) { + progress.setTotalDownloads(-1); + progress.setDescription("Checking for update"); + } + logger.info("Checking for update"); + URL releaseUrl = new URL(RELEASE_CHECK_URL); + try (InputStream in = releaseUrl.openStream(); + InputStreamReader reader = new InputStreamReader(in)) { + releaseData = new JsonParser().parse(reader).getAsJsonObject(); + JsonElement tagElement = releaseData.get("tag_name"); + if (tagElement == null) { + return false; + } + updateVersion = tagElement.getAsString(); + logger.info("Current version: " + VERSION); + logger.info("Latest version: " + updateVersion); + if (NetUtil.versionCompare(VERSION, updateVersion) < 0) { + logger.info("Update available"); + return true; + } + else { + logger.info("No update available"); + } + } + } + catch (IOException e) { + logger.severe("Failed to check for update\n" + e.getMessage()); + } + return false; + } + + private String getAssetUrl(String assetName) { + JsonArray array = releaseData.get("assets").getAsJsonArray(); + if (array == null) { + return null; + } + for (int i = 0; i < array.size(); ++i) { + JsonObject asset = array.get(i).getAsJsonObject(); + if (asset.get("name").getAsString().equals(assetName)) { + return asset.get("browser_download_url").getAsString(); + } + } + return null; + } + + private void getLibs(String urlStr, HashMap set) throws IOException { + URL url = new URL(urlStr); + try (InputStream is = url.openStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader reader = new BufferedReader(isr)) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.contains(LIBRARY_LINE_ID)) { + continue; + } + Matcher m = LIB_PATTERN.matcher(line); + if (!m.find()) { + continue; + } + line = m.group(); + String[] split = line.split(":"); + if (split.length != 3) { + throw new IOException("Invalid library list in remote repository"); + } + String groupId = split[0].replaceAll("\\.", "/"); + String artifactId = split[1]; + String version = split[2]; + String name = artifactId + "-" + version + ".jar"; + set.put(name, MAVEN_BASE_URL + groupId + "/" + artifactId + "/" + version + "/" + name); + } + } + } + + private HashMap getLibs() throws IOException { + HashMap libs = new HashMap<>(); + getLibs(REPO_BASE_URL + updateVersion + "/" + CLIENT.assetName, libs); + return libs; + } + + private boolean guessDevEnvironment() { + return !Update.class.getResource(Update.class.getSimpleName() + ".class").toString().toLowerCase().startsWith("jar:"); + } + + private void downloadFile(String urlStr, File destination) throws IOException { + logger.info("Downloading: " + urlStr); + URL url = new URL(urlStr); + FileUtils.copyURLToFile(url, destination, 10000, 10000); + } + + public boolean update() { + if (releaseData == null) { + return false; + } + if (guessDevEnvironment()) { + logger.warning("Legup appears to be running in a development environment so automatic updating has been disabled"); +// return false; + } + logger.info("Starting update"); + logger.info("Getting download list"); + if (progress != null) { + progress.setTotalDownloads(-1); + progress.setDescription("Starting update"); + } + String jarUrl = getAssetUrl(updateStream.assetName); + if (jarUrl == null) { + return false; + } + try { + HashMap libs = getLibs(); + if (!downloadDir.exists() && !downloadDir.mkdirs()) { + logger.warning("Failed to create temporary download directory"); + return false; + } + int current = 0; + if (progress != null) { + progress.setTotalDownloads(libs.size()); + progress.setCurrentDownload(current); + progress.setDescription("Downloading " + updateStream.assetName); + } + downloadFile(jarUrl, new File(downloadDir, updateStream.assetName)); + current++; + File libDir = new File(downloadDir, "lib"); + for (Map.Entry lib : libs.entrySet()) { + if (progress != null) { + progress.setCurrentDownload(current); + progress.setDescription("Downloading " + lib.getKey()); + } + downloadFile(lib.getValue(), new File(libDir, lib.getKey())); + current++; + } + if (progress != null) { + progress.setCurrentDownload(current); + progress.setDescription("Download complete!"); + } + logger.info("Download complete"); + return true; + } + catch (IOException e) { + logger.severe("Failed to update Legup\n" + e.getMessage()); + return false; + } + } + + private void unzipFile(File file) throws IOException { + try (ZipFile zipFile = new ZipFile(file)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + File entryDestination = new File(downloadDir, entry.getName()); + if (entry.isDirectory()) { + if (!entryDestination.exists() && !entryDestination.mkdirs()) { + throw new IOException("Failed to unzip file: " + file.getCanonicalPath()); + } + } + else { + if (!entryDestination.getParentFile().exists() && !entryDestination.getParentFile().mkdirs()) { + throw new IOException("Failed to unzip file: " + file.getCanonicalPath()); + } + try (InputStream in = zipFile.getInputStream(entry); + OutputStream out = new FileOutputStream(entryDestination)) { + IOUtils.copy(in, out); + } + } + } + } + } + + public void exit() { + System.exit(UPDATE_EXIT_CODE); + } + + public enum Stream { + CLIENT("Legup(1).jar", "client-update.zip"); + + public final String assetName; + public final String extraName; + + Stream(String assetName, String extraName) { + this.assetName = assetName; + this.extraName = extraName; + } + } + +} diff --git a/legup-update/src/main/java/edu/rpi/legupupdate/UpdateProgress.java b/legup-update/src/main/java/edu/rpi/legupupdate/UpdateProgress.java new file mode 100644 index 000000000..1c137ad04 --- /dev/null +++ b/legup-update/src/main/java/edu/rpi/legupupdate/UpdateProgress.java @@ -0,0 +1,11 @@ +package edu.rpi.legupupdate; + +public interface UpdateProgress { + + void setTotalDownloads(double total); + + void setCurrentDownload(double current); + + void setDescription(String description); + +} diff --git a/legup-update/src/main/resources/edu.rpi.legupupdate/VERSION b/legup-update/src/main/resources/edu.rpi.legupupdate/VERSION new file mode 100644 index 000000000..227cea215 --- /dev/null +++ b/legup-update/src/main/resources/edu.rpi.legupupdate/VERSION @@ -0,0 +1 @@ +2.0.0 diff --git a/puzzles files/fillapix/10x10 Fillapix Very Easy/03010000074 b/puzzles files/fillapix/10x10 Fillapix Very Easy/03010000074 index face5ef48..d514b73d0 100644 --- a/puzzles files/fillapix/10x10 Fillapix Very Easy/03010000074 +++ b/puzzles files/fillapix/10x10 Fillapix Very Easy/03010000074 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/lightup/10x10 Easy/1514228 b/puzzles files/lightup/10x10 Easy/1514228 index b60ba98df..4eb263016 100644 --- a/puzzles files/lightup/10x10 Easy/1514228 +++ b/puzzles files/lightup/10x10 Easy/1514228 @@ -30,5 +30,4 @@ - diff --git a/puzzles files/lightup/10x10 Easy/1721067 b/puzzles files/lightup/10x10 Easy/1721067 index 07665470d..a4b113ddb 100644 --- a/puzzles files/lightup/10x10 Easy/1721067 +++ b/puzzles files/lightup/10x10 Easy/1721067 @@ -30,5 +30,4 @@ - diff --git a/puzzles files/lightup/10x10 Easy/5403393 b/puzzles files/lightup/10x10 Easy/5403393 index 75b55ed99..7b316c678 100644 --- a/puzzles files/lightup/10x10 Easy/5403393 +++ b/puzzles files/lightup/10x10 Easy/5403393 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Easy/5774519 b/puzzles files/lightup/10x10 Easy/5774519 index fdcf54e2f..591d6366a 100644 --- a/puzzles files/lightup/10x10 Easy/5774519 +++ b/puzzles files/lightup/10x10 Easy/5774519 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Easy/954498 b/puzzles files/lightup/10x10 Easy/954498 index 852899d94..ef4680cb4 100644 --- a/puzzles files/lightup/10x10 Easy/954498 +++ b/puzzles files/lightup/10x10 Easy/954498 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Hard/5373598 b/puzzles files/lightup/10x10 Hard/5373598 index b7cc9791d..67e0bbdb9 100644 --- a/puzzles files/lightup/10x10 Hard/5373598 +++ b/puzzles files/lightup/10x10 Hard/5373598 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Hard/573404 b/puzzles files/lightup/10x10 Hard/573404 index 6e4f67a61..12d4ce960 100644 --- a/puzzles files/lightup/10x10 Hard/573404 +++ b/puzzles files/lightup/10x10 Hard/573404 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Hard/5787104 b/puzzles files/lightup/10x10 Hard/5787104 index 59a6f95db..03ef1fe87 100644 --- a/puzzles files/lightup/10x10 Hard/5787104 +++ b/puzzles files/lightup/10x10 Hard/5787104 @@ -30,5 +30,4 @@ - diff --git a/puzzles files/lightup/10x10 Hard/8003166 b/puzzles files/lightup/10x10 Hard/8003166 index 3ea9496d9..fe4cb461b 100644 --- a/puzzles files/lightup/10x10 Hard/8003166 +++ b/puzzles files/lightup/10x10 Hard/8003166 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Hard/8918995 b/puzzles files/lightup/10x10 Hard/8918995 index f89fcce5d..22c95573a 100644 --- a/puzzles files/lightup/10x10 Hard/8918995 +++ b/puzzles files/lightup/10x10 Hard/8918995 @@ -34,5 +34,4 @@ - diff --git a/puzzles files/lightup/10x10 Normal/2761230 b/puzzles files/lightup/10x10 Normal/2761230 index 32a8e2b7b..c2f5dee6f 100644 --- a/puzzles files/lightup/10x10 Normal/2761230 +++ b/puzzles files/lightup/10x10 Normal/2761230 @@ -30,5 +30,4 @@ - diff --git a/puzzles files/lightup/10x10 Normal/343176 b/puzzles files/lightup/10x10 Normal/343176 index 7b34df834..2a6160327 100644 --- a/puzzles files/lightup/10x10 Normal/343176 +++ b/puzzles files/lightup/10x10 Normal/343176 @@ -30,5 +30,4 @@ - diff --git a/puzzles files/lightup/10x10 Normal/632466 b/puzzles files/lightup/10x10 Normal/632466 index 7501eb7ef..f8a6c6d99 100644 --- a/puzzles files/lightup/10x10 Normal/632466 +++ b/puzzles files/lightup/10x10 Normal/632466 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Normal/7752941 b/puzzles files/lightup/10x10 Normal/7752941 index 100de78cd..52397a6b2 100644 --- a/puzzles files/lightup/10x10 Normal/7752941 +++ b/puzzles files/lightup/10x10 Normal/7752941 @@ -26,5 +26,4 @@ - diff --git a/puzzles files/lightup/10x10 Normal/8090631 b/puzzles files/lightup/10x10 Normal/8090631 index f0ea906e9..f481414c9 100644 --- a/puzzles files/lightup/10x10 Normal/8090631 +++ b/puzzles files/lightup/10x10 Normal/8090631 @@ -30,5 +30,4 @@ - diff --git a/puzzles files/lightup/14x14 Easy/1412335 b/puzzles files/lightup/14x14 Easy/1412335 index 29863daae..5959d9bb6 100644 --- a/puzzles files/lightup/14x14 Easy/1412335 +++ b/puzzles files/lightup/14x14 Easy/1412335 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Easy/1949915 b/puzzles files/lightup/14x14 Easy/1949915 index f11c997f5..3742cdba8 100644 --- a/puzzles files/lightup/14x14 Easy/1949915 +++ b/puzzles files/lightup/14x14 Easy/1949915 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Easy/6778348 b/puzzles files/lightup/14x14 Easy/6778348 index a2025b627..074f1d836 100644 --- a/puzzles files/lightup/14x14 Easy/6778348 +++ b/puzzles files/lightup/14x14 Easy/6778348 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Easy/976495 b/puzzles files/lightup/14x14 Easy/976495 index 4abc0813a..7321a5b87 100644 --- a/puzzles files/lightup/14x14 Easy/976495 +++ b/puzzles files/lightup/14x14 Easy/976495 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Easy/9949966 b/puzzles files/lightup/14x14 Easy/9949966 index 9a363631c..8ae7d5abe 100644 --- a/puzzles files/lightup/14x14 Easy/9949966 +++ b/puzzles files/lightup/14x14 Easy/9949966 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/lightup/14x14 Hard/1974912 b/puzzles files/lightup/14x14 Hard/1974912 index 9174bbd50..3ac9c44a6 100644 --- a/puzzles files/lightup/14x14 Hard/1974912 +++ b/puzzles files/lightup/14x14 Hard/1974912 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Hard/3618696 b/puzzles files/lightup/14x14 Hard/3618696 index 5e555d8ec..91f02e1ba 100644 --- a/puzzles files/lightup/14x14 Hard/3618696 +++ b/puzzles files/lightup/14x14 Hard/3618696 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Hard/578987 b/puzzles files/lightup/14x14 Hard/578987 index 281effef3..3ccfaa1e7 100644 --- a/puzzles files/lightup/14x14 Hard/578987 +++ b/puzzles files/lightup/14x14 Hard/578987 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Hard/8087653 b/puzzles files/lightup/14x14 Hard/8087653 index dfd8f161c..1f7c77e99 100644 --- a/puzzles files/lightup/14x14 Hard/8087653 +++ b/puzzles files/lightup/14x14 Hard/8087653 @@ -62,5 +62,4 @@ - diff --git a/puzzles files/lightup/14x14 Hard/9554192 b/puzzles files/lightup/14x14 Hard/9554192 index c0ef178d7..120adddb2 100644 --- a/puzzles files/lightup/14x14 Hard/9554192 +++ b/puzzles files/lightup/14x14 Hard/9554192 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Normal/2659779 b/puzzles files/lightup/14x14 Normal/2659779 index e54876bfa..d367b6c65 100644 --- a/puzzles files/lightup/14x14 Normal/2659779 +++ b/puzzles files/lightup/14x14 Normal/2659779 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Normal/448333 b/puzzles files/lightup/14x14 Normal/448333 index 3a1a2b999..b93938bfe 100644 --- a/puzzles files/lightup/14x14 Normal/448333 +++ b/puzzles files/lightup/14x14 Normal/448333 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Normal/5063453 b/puzzles files/lightup/14x14 Normal/5063453 index 579622ab9..e3cc8578c 100644 --- a/puzzles files/lightup/14x14 Normal/5063453 +++ b/puzzles files/lightup/14x14 Normal/5063453 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/lightup/14x14 Normal/606275 b/puzzles files/lightup/14x14 Normal/606275 index 377747988..0b5bb6590 100644 --- a/puzzles files/lightup/14x14 Normal/606275 +++ b/puzzles files/lightup/14x14 Normal/606275 @@ -54,5 +54,4 @@ - diff --git a/puzzles files/lightup/14x14 Normal/830231 b/puzzles files/lightup/14x14 Normal/830231 index cfc8fbf43..118c41d24 100644 --- a/puzzles files/lightup/14x14 Normal/830231 +++ b/puzzles files/lightup/14x14 Normal/830231 @@ -61,5 +61,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/2408448 b/puzzles files/lightup/7x7 Easy/2408448 index de29dc419..b8fe2d7fe 100644 --- a/puzzles files/lightup/7x7 Easy/2408448 +++ b/puzzles files/lightup/7x7 Easy/2408448 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/2736784 b/puzzles files/lightup/7x7 Easy/2736784 index a5041796e..1160ced8b 100644 --- a/puzzles files/lightup/7x7 Easy/2736784 +++ b/puzzles files/lightup/7x7 Easy/2736784 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/2855683 b/puzzles files/lightup/7x7 Easy/2855683 index fc84f34b8..47e9cff0d 100644 --- a/puzzles files/lightup/7x7 Easy/2855683 +++ b/puzzles files/lightup/7x7 Easy/2855683 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/3535701 b/puzzles files/lightup/7x7 Easy/3535701 index 7a1e79aaa..27b4be065 100644 --- a/puzzles files/lightup/7x7 Easy/3535701 +++ b/puzzles files/lightup/7x7 Easy/3535701 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/393454 b/puzzles files/lightup/7x7 Easy/393454 index f10385f1c..039718e13 100644 --- a/puzzles files/lightup/7x7 Easy/393454 +++ b/puzzles files/lightup/7x7 Easy/393454 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/4238934 b/puzzles files/lightup/7x7 Easy/4238934 index 6106f8f73..58108606d 100644 --- a/puzzles files/lightup/7x7 Easy/4238934 +++ b/puzzles files/lightup/7x7 Easy/4238934 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/4604288 b/puzzles files/lightup/7x7 Easy/4604288 index c74b7a07a..0cc8d862a 100644 --- a/puzzles files/lightup/7x7 Easy/4604288 +++ b/puzzles files/lightup/7x7 Easy/4604288 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/4608986 b/puzzles files/lightup/7x7 Easy/4608986 index b80712533..eb6fd1405 100644 --- a/puzzles files/lightup/7x7 Easy/4608986 +++ b/puzzles files/lightup/7x7 Easy/4608986 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/517362 b/puzzles files/lightup/7x7 Easy/517362 index e2242e266..a6b7c32e7 100644 --- a/puzzles files/lightup/7x7 Easy/517362 +++ b/puzzles files/lightup/7x7 Easy/517362 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/5229613 b/puzzles files/lightup/7x7 Easy/5229613 index 78fa909ac..f755e6a27 100644 --- a/puzzles files/lightup/7x7 Easy/5229613 +++ b/puzzles files/lightup/7x7 Easy/5229613 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/5435528 b/puzzles files/lightup/7x7 Easy/5435528 index a34ef0587..6c5c133d1 100644 --- a/puzzles files/lightup/7x7 Easy/5435528 +++ b/puzzles files/lightup/7x7 Easy/5435528 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/5488697 b/puzzles files/lightup/7x7 Easy/5488697 index 538665943..18c21eb4f 100644 --- a/puzzles files/lightup/7x7 Easy/5488697 +++ b/puzzles files/lightup/7x7 Easy/5488697 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/6110408 b/puzzles files/lightup/7x7 Easy/6110408 index 8905837d8..302dfb1a7 100644 --- a/puzzles files/lightup/7x7 Easy/6110408 +++ b/puzzles files/lightup/7x7 Easy/6110408 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/6300100 b/puzzles files/lightup/7x7 Easy/6300100 index 4e4578de6..37855feba 100644 --- a/puzzles files/lightup/7x7 Easy/6300100 +++ b/puzzles files/lightup/7x7 Easy/6300100 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/6506194 b/puzzles files/lightup/7x7 Easy/6506194 index 1feeead80..a21e6b9ec 100644 --- a/puzzles files/lightup/7x7 Easy/6506194 +++ b/puzzles files/lightup/7x7 Easy/6506194 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/69495 b/puzzles files/lightup/7x7 Easy/69495 index 71a0914c5..a3b048963 100644 --- a/puzzles files/lightup/7x7 Easy/69495 +++ b/puzzles files/lightup/7x7 Easy/69495 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/7936304 b/puzzles files/lightup/7x7 Easy/7936304 index 3fdc7c5f1..bd4a81f6d 100644 --- a/puzzles files/lightup/7x7 Easy/7936304 +++ b/puzzles files/lightup/7x7 Easy/7936304 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/7982513 b/puzzles files/lightup/7x7 Easy/7982513 index 4246b04e1..d140be9cf 100644 --- a/puzzles files/lightup/7x7 Easy/7982513 +++ b/puzzles files/lightup/7x7 Easy/7982513 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/8949430 b/puzzles files/lightup/7x7 Easy/8949430 index 45eb4e763..a4c87729f 100644 --- a/puzzles files/lightup/7x7 Easy/8949430 +++ b/puzzles files/lightup/7x7 Easy/8949430 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Easy/9080685 b/puzzles files/lightup/7x7 Easy/9080685 index d1071de3b..a5ef8e22f 100644 --- a/puzzles files/lightup/7x7 Easy/9080685 +++ b/puzzles files/lightup/7x7 Easy/9080685 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/1130370 b/puzzles files/lightup/7x7 Hard/1130370 index a5d6ab650..afab8ece1 100644 --- a/puzzles files/lightup/7x7 Hard/1130370 +++ b/puzzles files/lightup/7x7 Hard/1130370 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/2790971 b/puzzles files/lightup/7x7 Hard/2790971 index 87b7dc5fc..2f91d1aff 100644 --- a/puzzles files/lightup/7x7 Hard/2790971 +++ b/puzzles files/lightup/7x7 Hard/2790971 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/3421347 b/puzzles files/lightup/7x7 Hard/3421347 index 68c6cb4fe..f16a9ecf1 100644 --- a/puzzles files/lightup/7x7 Hard/3421347 +++ b/puzzles files/lightup/7x7 Hard/3421347 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/4159457 b/puzzles files/lightup/7x7 Hard/4159457 index 05b3a0bc5..d08b1c012 100644 --- a/puzzles files/lightup/7x7 Hard/4159457 +++ b/puzzles files/lightup/7x7 Hard/4159457 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/4674087 b/puzzles files/lightup/7x7 Hard/4674087 index ed4dd0002..ca79fb87f 100644 --- a/puzzles files/lightup/7x7 Hard/4674087 +++ b/puzzles files/lightup/7x7 Hard/4674087 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/507817 b/puzzles files/lightup/7x7 Hard/507817 index 0bedc7cd5..64a53c371 100644 --- a/puzzles files/lightup/7x7 Hard/507817 +++ b/puzzles files/lightup/7x7 Hard/507817 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/5280094 b/puzzles files/lightup/7x7 Hard/5280094 index 36e4fe127..cda81c889 100644 --- a/puzzles files/lightup/7x7 Hard/5280094 +++ b/puzzles files/lightup/7x7 Hard/5280094 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/5677803 b/puzzles files/lightup/7x7 Hard/5677803 index 6fa56c0d9..d7fee9e79 100644 --- a/puzzles files/lightup/7x7 Hard/5677803 +++ b/puzzles files/lightup/7x7 Hard/5677803 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/6178908 b/puzzles files/lightup/7x7 Hard/6178908 index 0cb8be391..bc3febaf0 100644 --- a/puzzles files/lightup/7x7 Hard/6178908 +++ b/puzzles files/lightup/7x7 Hard/6178908 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Hard/8122162 b/puzzles files/lightup/7x7 Hard/8122162 index 539fb9641..cfd808ccf 100644 --- a/puzzles files/lightup/7x7 Hard/8122162 +++ b/puzzles files/lightup/7x7 Hard/8122162 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/2637310 b/puzzles files/lightup/7x7 Normal/2637310 index 8fd9d073b..c1e88ea4f 100644 --- a/puzzles files/lightup/7x7 Normal/2637310 +++ b/puzzles files/lightup/7x7 Normal/2637310 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/2979943 b/puzzles files/lightup/7x7 Normal/2979943 index 6cf281463..7164eff19 100644 --- a/puzzles files/lightup/7x7 Normal/2979943 +++ b/puzzles files/lightup/7x7 Normal/2979943 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/3710905 b/puzzles files/lightup/7x7 Normal/3710905 index 235b91e00..9ae7304dd 100644 --- a/puzzles files/lightup/7x7 Normal/3710905 +++ b/puzzles files/lightup/7x7 Normal/3710905 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/3727425 b/puzzles files/lightup/7x7 Normal/3727425 index c06aa1903..96495d463 100644 --- a/puzzles files/lightup/7x7 Normal/3727425 +++ b/puzzles files/lightup/7x7 Normal/3727425 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/3787583 b/puzzles files/lightup/7x7 Normal/3787583 index 192fe831d..a3972a249 100644 --- a/puzzles files/lightup/7x7 Normal/3787583 +++ b/puzzles files/lightup/7x7 Normal/3787583 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/5570754 b/puzzles files/lightup/7x7 Normal/5570754 index 01e6224cd..5e94bc9c3 100644 --- a/puzzles files/lightup/7x7 Normal/5570754 +++ b/puzzles files/lightup/7x7 Normal/5570754 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/7270504 b/puzzles files/lightup/7x7 Normal/7270504 index 57d3f0fa8..a5a539f40 100644 --- a/puzzles files/lightup/7x7 Normal/7270504 +++ b/puzzles files/lightup/7x7 Normal/7270504 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/8000000 b/puzzles files/lightup/7x7 Normal/8000000 index 6de8dd8ba..bad645ee2 100644 --- a/puzzles files/lightup/7x7 Normal/8000000 +++ b/puzzles files/lightup/7x7 Normal/8000000 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/9489812 b/puzzles files/lightup/7x7 Normal/9489812 index fb5bd3113..e342523b8 100644 --- a/puzzles files/lightup/7x7 Normal/9489812 +++ b/puzzles files/lightup/7x7 Normal/9489812 @@ -15,5 +15,4 @@ - diff --git a/puzzles files/lightup/7x7 Normal/9806740 b/puzzles files/lightup/7x7 Normal/9806740 index f3493a021..cd87ca7dc 100644 --- a/puzzles files/lightup/7x7 Normal/9806740 +++ b/puzzles files/lightup/7x7 Normal/9806740 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Easy/6E_b0011 b/puzzles files/masyu/6x6 Masyu Easy/6E_b0011 index 637567c9f..d88dc4abb 100644 --- a/puzzles files/masyu/6x6 Masyu Easy/6E_b0011 +++ b/puzzles files/masyu/6x6 Masyu Easy/6E_b0011 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Easy/6E_b0012 b/puzzles files/masyu/6x6 Masyu Easy/6E_b0012 index 304f51bd8..ea47166ce 100644 --- a/puzzles files/masyu/6x6 Masyu Easy/6E_b0012 +++ b/puzzles files/masyu/6x6 Masyu Easy/6E_b0012 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Easy/6E_b0013 b/puzzles files/masyu/6x6 Masyu Easy/6E_b0013 index 4c8c5171a..af7040555 100644 --- a/puzzles files/masyu/6x6 Masyu Easy/6E_b0013 +++ b/puzzles files/masyu/6x6 Masyu Easy/6E_b0013 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Easy/6E_b0014 b/puzzles files/masyu/6x6 Masyu Easy/6E_b0014 index 209eb2863..a02ef4068 100644 --- a/puzzles files/masyu/6x6 Masyu Easy/6E_b0014 +++ b/puzzles files/masyu/6x6 Masyu Easy/6E_b0014 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Easy/6E_b0015 b/puzzles files/masyu/6x6 Masyu Easy/6E_b0015 index 6ec496107..9e66c9eb1 100644 --- a/puzzles files/masyu/6x6 Masyu Easy/6E_b0015 +++ b/puzzles files/masyu/6x6 Masyu Easy/6E_b0015 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Hard/6H_b0011 b/puzzles files/masyu/6x6 Masyu Hard/6H_b0011 index bafaa7bdb..4e4ddc3bc 100644 --- a/puzzles files/masyu/6x6 Masyu Hard/6H_b0011 +++ b/puzzles files/masyu/6x6 Masyu Hard/6H_b0011 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Hard/6H_b0012 b/puzzles files/masyu/6x6 Masyu Hard/6H_b0012 index 15212f451..57b5ace30 100644 --- a/puzzles files/masyu/6x6 Masyu Hard/6H_b0012 +++ b/puzzles files/masyu/6x6 Masyu Hard/6H_b0012 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Hard/6H_b0013 b/puzzles files/masyu/6x6 Masyu Hard/6H_b0013 index 9c5b410bd..ed78214e7 100644 --- a/puzzles files/masyu/6x6 Masyu Hard/6H_b0013 +++ b/puzzles files/masyu/6x6 Masyu Hard/6H_b0013 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Hard/6H_b0014 b/puzzles files/masyu/6x6 Masyu Hard/6H_b0014 index c271db9a4..24cd73827 100644 --- a/puzzles files/masyu/6x6 Masyu Hard/6H_b0014 +++ b/puzzles files/masyu/6x6 Masyu Hard/6H_b0014 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Hard/6H_b0015 b/puzzles files/masyu/6x6 Masyu Hard/6H_b0015 index ae37a1e41..30bb3ecf2 100644 --- a/puzzles files/masyu/6x6 Masyu Hard/6H_b0015 +++ b/puzzles files/masyu/6x6 Masyu Hard/6H_b0015 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Medium/6M_b0011 b/puzzles files/masyu/6x6 Masyu Medium/6M_b0011 index bc378de72..3dcd13d61 100644 --- a/puzzles files/masyu/6x6 Masyu Medium/6M_b0011 +++ b/puzzles files/masyu/6x6 Masyu Medium/6M_b0011 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Medium/6M_b0012 b/puzzles files/masyu/6x6 Masyu Medium/6M_b0012 index 564245639..52a5e16ad 100644 --- a/puzzles files/masyu/6x6 Masyu Medium/6M_b0012 +++ b/puzzles files/masyu/6x6 Masyu Medium/6M_b0012 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Medium/6M_b0013 b/puzzles files/masyu/6x6 Masyu Medium/6M_b0013 index 388908083..e7ccb2980 100644 --- a/puzzles files/masyu/6x6 Masyu Medium/6M_b0013 +++ b/puzzles files/masyu/6x6 Masyu Medium/6M_b0013 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Medium/6M_b0014 b/puzzles files/masyu/6x6 Masyu Medium/6M_b0014 index 01b2baafa..3274804b3 100644 --- a/puzzles files/masyu/6x6 Masyu Medium/6M_b0014 +++ b/puzzles files/masyu/6x6 Masyu Medium/6M_b0014 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/masyu/6x6 Masyu Medium/6M_b0015 b/puzzles files/masyu/6x6 Masyu Medium/6M_b0015 index cf246c891..a645a3936 100644 --- a/puzzles files/masyu/6x6 Masyu Medium/6M_b0015 +++ b/puzzles files/masyu/6x6 Masyu Medium/6M_b0015 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/narukabe_export tet b/puzzles files/narukabe_export tet index b24ae3073..9a76535e0 100644 --- a/puzzles files/narukabe_export tet +++ b/puzzles files/narukabe_export tet @@ -1,32 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/nurikabe/10x10 Nurikabe Hard/3323808 b/puzzles files/nurikabe/10x10 Nurikabe Hard/3323808 index 755fb9411..ffdfee88f 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Hard/3323808 +++ b/puzzles files/nurikabe/10x10 Nurikabe Hard/3323808 @@ -17,5 +17,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Hard/7675382 b/puzzles files/nurikabe/10x10 Nurikabe Hard/7675382 index 87681726a..414f9bb47 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Hard/7675382 +++ b/puzzles files/nurikabe/10x10 Nurikabe Hard/7675382 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Hard/8264437 b/puzzles files/nurikabe/10x10 Nurikabe Hard/8264437 index 92fc60999..e3d993ca7 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Hard/8264437 +++ b/puzzles files/nurikabe/10x10 Nurikabe Hard/8264437 @@ -21,5 +21,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Hard/8991218 b/puzzles files/nurikabe/10x10 Nurikabe Hard/8991218 index 52babddd6..f707c9edb 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Hard/8991218 +++ b/puzzles files/nurikabe/10x10 Nurikabe Hard/8991218 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Hard/9120893 b/puzzles files/nurikabe/10x10 Nurikabe Hard/9120893 index c846329f9..d30061ab6 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Hard/9120893 +++ b/puzzles files/nurikabe/10x10 Nurikabe Hard/9120893 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Normal/2852229 b/puzzles files/nurikabe/10x10 Nurikabe Normal/2852229 index 159d5d405..7752cc3a9 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Normal/2852229 +++ b/puzzles files/nurikabe/10x10 Nurikabe Normal/2852229 @@ -18,5 +18,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Normal/5759900 b/puzzles files/nurikabe/10x10 Nurikabe Normal/5759900 index 064b04617..4f9eaeb45 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Normal/5759900 +++ b/puzzles files/nurikabe/10x10 Nurikabe Normal/5759900 @@ -19,5 +19,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Normal/6274292 b/puzzles files/nurikabe/10x10 Nurikabe Normal/6274292 index 834e61259..ccffd35da 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Normal/6274292 +++ b/puzzles files/nurikabe/10x10 Nurikabe Normal/6274292 @@ -22,5 +22,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Normal/8920015 b/puzzles files/nurikabe/10x10 Nurikabe Normal/8920015 index 4ac1c391a..273dc5ecc 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Normal/8920015 +++ b/puzzles files/nurikabe/10x10 Nurikabe Normal/8920015 @@ -22,5 +22,4 @@ - diff --git a/puzzles files/nurikabe/10x10 Nurikabe Normal/9757357 b/puzzles files/nurikabe/10x10 Nurikabe Normal/9757357 index a014eb32d..da3ab0a74 100644 --- a/puzzles files/nurikabe/10x10 Nurikabe Normal/9757357 +++ b/puzzles files/nurikabe/10x10 Nurikabe Normal/9757357 @@ -17,5 +17,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Hard/1276535 b/puzzles files/nurikabe/12x12 Nurikabe Hard/1276535 index 70fbdfe8a..bbfa5ed1f 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Hard/1276535 +++ b/puzzles files/nurikabe/12x12 Nurikabe Hard/1276535 @@ -22,5 +22,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Hard/4284912 b/puzzles files/nurikabe/12x12 Nurikabe Hard/4284912 index bdc358496..401e68fb7 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Hard/4284912 +++ b/puzzles files/nurikabe/12x12 Nurikabe Hard/4284912 @@ -22,5 +22,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Hard/4459392 b/puzzles files/nurikabe/12x12 Nurikabe Hard/4459392 index 949683386..a715a8bca 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Hard/4459392 +++ b/puzzles files/nurikabe/12x12 Nurikabe Hard/4459392 @@ -23,5 +23,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Hard/7738199 b/puzzles files/nurikabe/12x12 Nurikabe Hard/7738199 index 107b1f1a8..bba566af4 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Hard/7738199 +++ b/puzzles files/nurikabe/12x12 Nurikabe Hard/7738199 @@ -21,5 +21,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Hard/9205907 b/puzzles files/nurikabe/12x12 Nurikabe Hard/9205907 index 185f1959b..3757c89c9 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Hard/9205907 +++ b/puzzles files/nurikabe/12x12 Nurikabe Hard/9205907 @@ -22,5 +22,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Normal/1570101 b/puzzles files/nurikabe/12x12 Nurikabe Normal/1570101 index 74cdcc65a..f1eef204e 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Normal/1570101 +++ b/puzzles files/nurikabe/12x12 Nurikabe Normal/1570101 @@ -24,5 +24,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Normal/3731930 b/puzzles files/nurikabe/12x12 Nurikabe Normal/3731930 index 071d482de..67114d31e 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Normal/3731930 +++ b/puzzles files/nurikabe/12x12 Nurikabe Normal/3731930 @@ -27,5 +27,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Normal/3755957 b/puzzles files/nurikabe/12x12 Nurikabe Normal/3755957 index 10313d422..04cd4c0ce 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Normal/3755957 +++ b/puzzles files/nurikabe/12x12 Nurikabe Normal/3755957 @@ -24,5 +24,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Normal/786806 b/puzzles files/nurikabe/12x12 Nurikabe Normal/786806 index c0d9d97f6..99f2ff3a2 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Normal/786806 +++ b/puzzles files/nurikabe/12x12 Nurikabe Normal/786806 @@ -23,5 +23,4 @@ - diff --git a/puzzles files/nurikabe/12x12 Nurikabe Normal/9946063 b/puzzles files/nurikabe/12x12 Nurikabe Normal/9946063 index 5746d1373..b82e4ff98 100644 --- a/puzzles files/nurikabe/12x12 Nurikabe Normal/9946063 +++ b/puzzles files/nurikabe/12x12 Nurikabe Normal/9946063 @@ -22,5 +22,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Hard/1125631 b/puzzles files/nurikabe/15x15 Nurikabe Hard/1125631 index 04c045ce8..65ea67b8c 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Hard/1125631 +++ b/puzzles files/nurikabe/15x15 Nurikabe Hard/1125631 @@ -40,5 +40,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Hard/5955292 b/puzzles files/nurikabe/15x15 Nurikabe Hard/5955292 index 2dc656c36..d086a352a 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Hard/5955292 +++ b/puzzles files/nurikabe/15x15 Nurikabe Hard/5955292 @@ -38,5 +38,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Hard/6763936 b/puzzles files/nurikabe/15x15 Nurikabe Hard/6763936 index 4be19301f..ba2d0a248 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Hard/6763936 +++ b/puzzles files/nurikabe/15x15 Nurikabe Hard/6763936 @@ -38,5 +38,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Hard/7005298 b/puzzles files/nurikabe/15x15 Nurikabe Hard/7005298 index 79d16ffd0..6270e7930 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Hard/7005298 +++ b/puzzles files/nurikabe/15x15 Nurikabe Hard/7005298 @@ -40,5 +40,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Hard/9975093 b/puzzles files/nurikabe/15x15 Nurikabe Hard/9975093 index bf7cf35ef..92b2e2212 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Hard/9975093 +++ b/puzzles files/nurikabe/15x15 Nurikabe Hard/9975093 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Normal/141420 b/puzzles files/nurikabe/15x15 Nurikabe Normal/141420 index affb5d2e9..9897588c8 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Normal/141420 +++ b/puzzles files/nurikabe/15x15 Nurikabe Normal/141420 @@ -46,5 +46,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Normal/4123443 b/puzzles files/nurikabe/15x15 Nurikabe Normal/4123443 index 0acf06e84..f92577c26 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Normal/4123443 +++ b/puzzles files/nurikabe/15x15 Nurikabe Normal/4123443 @@ -42,5 +42,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Normal/731385 b/puzzles files/nurikabe/15x15 Nurikabe Normal/731385 index db29874c5..b1dfd7580 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Normal/731385 +++ b/puzzles files/nurikabe/15x15 Nurikabe Normal/731385 @@ -41,5 +41,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Normal/8213677 b/puzzles files/nurikabe/15x15 Nurikabe Normal/8213677 index 62c27f67d..f9b4c63b4 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Normal/8213677 +++ b/puzzles files/nurikabe/15x15 Nurikabe Normal/8213677 @@ -41,5 +41,4 @@ - diff --git a/puzzles files/nurikabe/15x15 Nurikabe Normal/8372309 b/puzzles files/nurikabe/15x15 Nurikabe Normal/8372309 index cf2728b1a..97a6eae91 100644 --- a/puzzles files/nurikabe/15x15 Nurikabe Normal/8372309 +++ b/puzzles files/nurikabe/15x15 Nurikabe Normal/8372309 @@ -45,5 +45,4 @@ - diff --git a/puzzles files/nurikabe/20x20 Nurikabe Normal/175081 b/puzzles files/nurikabe/20x20 Nurikabe Normal/175081 index 67c0ed170..bfd66a5be 100644 --- a/puzzles files/nurikabe/20x20 Nurikabe Normal/175081 +++ b/puzzles files/nurikabe/20x20 Nurikabe Normal/175081 @@ -76,5 +76,4 @@ - diff --git a/puzzles files/nurikabe/20x20 Nurikabe Normal/3131243 b/puzzles files/nurikabe/20x20 Nurikabe Normal/3131243 index 8b9ade2de..7f85c4b35 100644 --- a/puzzles files/nurikabe/20x20 Nurikabe Normal/3131243 +++ b/puzzles files/nurikabe/20x20 Nurikabe Normal/3131243 @@ -81,5 +81,4 @@ - diff --git a/puzzles files/nurikabe/20x20 Nurikabe Normal/4400487 b/puzzles files/nurikabe/20x20 Nurikabe Normal/4400487 index 143d1a6c9..1c65e93f3 100644 --- a/puzzles files/nurikabe/20x20 Nurikabe Normal/4400487 +++ b/puzzles files/nurikabe/20x20 Nurikabe Normal/4400487 @@ -75,5 +75,4 @@ - diff --git a/puzzles files/nurikabe/20x20 Nurikabe Normal/5096090 b/puzzles files/nurikabe/20x20 Nurikabe Normal/5096090 index 47ec237e4..f8ad5b129 100644 --- a/puzzles files/nurikabe/20x20 Nurikabe Normal/5096090 +++ b/puzzles files/nurikabe/20x20 Nurikabe Normal/5096090 @@ -76,5 +76,4 @@ - diff --git a/puzzles files/nurikabe/20x20 Nurikabe Normal/8595641 b/puzzles files/nurikabe/20x20 Nurikabe Normal/8595641 index 82df8e7a7..84f1c9f08 100644 --- a/puzzles files/nurikabe/20x20 Nurikabe Normal/8595641 +++ b/puzzles files/nurikabe/20x20 Nurikabe Normal/8595641 @@ -79,5 +79,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Easy/118040 b/puzzles files/nurikabe/5x5 Nurikabe Easy/118040 index 29b2585e1..fdd9c438f 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Easy/118040 +++ b/puzzles files/nurikabe/5x5 Nurikabe Easy/118040 @@ -11,5 +11,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Easy/235235 b/puzzles files/nurikabe/5x5 Nurikabe Easy/235235 index 59ca2a706..635f48456 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Easy/235235 +++ b/puzzles files/nurikabe/5x5 Nurikabe Easy/235235 @@ -10,5 +10,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Hard/118040 b/puzzles files/nurikabe/5x5 Nurikabe Hard/118040 index 8b6c004e3..abff61ac5 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Hard/118040 +++ b/puzzles files/nurikabe/5x5 Nurikabe Hard/118040 @@ -9,5 +9,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Hard/1726232 b/puzzles files/nurikabe/5x5 Nurikabe Hard/1726232 index a28ba98fc..f3e6785a9 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Hard/1726232 +++ b/puzzles files/nurikabe/5x5 Nurikabe Hard/1726232 @@ -9,5 +9,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Hard/2168054 b/puzzles files/nurikabe/5x5 Nurikabe Hard/2168054 index 1ed31751c..f9f4e6a5e 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Hard/2168054 +++ b/puzzles files/nurikabe/5x5 Nurikabe Hard/2168054 @@ -9,5 +9,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Hard/7209030 b/puzzles files/nurikabe/5x5 Nurikabe Hard/7209030 index 2b1d486c4..e179e74ad 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Hard/7209030 +++ b/puzzles files/nurikabe/5x5 Nurikabe Hard/7209030 @@ -10,5 +10,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Hard/7897030 b/puzzles files/nurikabe/5x5 Nurikabe Hard/7897030 index 64b4b6819..b6d770487 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Hard/7897030 +++ b/puzzles files/nurikabe/5x5 Nurikabe Hard/7897030 @@ -10,5 +10,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Normal/1795638 b/puzzles files/nurikabe/5x5 Nurikabe Normal/1795638 index 9ae7d9480..509569d0b 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Normal/1795638 +++ b/puzzles files/nurikabe/5x5 Nurikabe Normal/1795638 @@ -10,5 +10,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Normal/2663893 b/puzzles files/nurikabe/5x5 Nurikabe Normal/2663893 index 3a2c2c411..75b801777 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Normal/2663893 +++ b/puzzles files/nurikabe/5x5 Nurikabe Normal/2663893 @@ -10,5 +10,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Normal/3282122 b/puzzles files/nurikabe/5x5 Nurikabe Normal/3282122 index 6f3316b3b..3f0c5c591 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Normal/3282122 +++ b/puzzles files/nurikabe/5x5 Nurikabe Normal/3282122 @@ -10,5 +10,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Normal/6403086 b/puzzles files/nurikabe/5x5 Nurikabe Normal/6403086 index c0b7a51be..1f0d68b19 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Normal/6403086 +++ b/puzzles files/nurikabe/5x5 Nurikabe Normal/6403086 @@ -12,5 +12,4 @@ - diff --git a/puzzles files/nurikabe/5x5 Nurikabe Normal/7587733 b/puzzles files/nurikabe/5x5 Nurikabe Normal/7587733 index 0ed10b168..26995e095 100644 --- a/puzzles files/nurikabe/5x5 Nurikabe Normal/7587733 +++ b/puzzles files/nurikabe/5x5 Nurikabe Normal/7587733 @@ -11,5 +11,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Hard/4478248 b/puzzles files/nurikabe/7x7 Nurikabe Hard/4478248 index 724c71cf0..6db0bf4fd 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Hard/4478248 +++ b/puzzles files/nurikabe/7x7 Nurikabe Hard/4478248 @@ -11,5 +11,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Hard/5631322 b/puzzles files/nurikabe/7x7 Nurikabe Hard/5631322 index 87220741e..f8478f601 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Hard/5631322 +++ b/puzzles files/nurikabe/7x7 Nurikabe Hard/5631322 @@ -12,5 +12,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Hard/6734769 b/puzzles files/nurikabe/7x7 Nurikabe Hard/6734769 index c74abb042..a85a959e4 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Hard/6734769 +++ b/puzzles files/nurikabe/7x7 Nurikabe Hard/6734769 @@ -12,5 +12,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Hard/8078467 b/puzzles files/nurikabe/7x7 Nurikabe Hard/8078467 index 4212e5748..7cb47794c 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Hard/8078467 +++ b/puzzles files/nurikabe/7x7 Nurikabe Hard/8078467 @@ -12,5 +12,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Hard/9225348 b/puzzles files/nurikabe/7x7 Nurikabe Hard/9225348 index a34516063..49d0def37 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Hard/9225348 +++ b/puzzles files/nurikabe/7x7 Nurikabe Hard/9225348 @@ -11,5 +11,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Normal/42455 b/puzzles files/nurikabe/7x7 Nurikabe Normal/42455 index 9e2f4b265..abc674184 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Normal/42455 +++ b/puzzles files/nurikabe/7x7 Nurikabe Normal/42455 @@ -11,5 +11,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Normal/4390561 b/puzzles files/nurikabe/7x7 Nurikabe Normal/4390561 index d69bcb1f0..7f802752b 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Normal/4390561 +++ b/puzzles files/nurikabe/7x7 Nurikabe Normal/4390561 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Normal/4877172 b/puzzles files/nurikabe/7x7 Nurikabe Normal/4877172 index f9e72c054..c67b48c7b 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Normal/4877172 +++ b/puzzles files/nurikabe/7x7 Nurikabe Normal/4877172 @@ -14,5 +14,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Normal/7958242 b/puzzles files/nurikabe/7x7 Nurikabe Normal/7958242 index 0adc01651..f7eaab0b1 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Normal/7958242 +++ b/puzzles files/nurikabe/7x7 Nurikabe Normal/7958242 @@ -12,5 +12,4 @@ - diff --git a/puzzles files/nurikabe/7x7 Nurikabe Normal/8786625 b/puzzles files/nurikabe/7x7 Nurikabe Normal/8786625 index 7f7553915..c6e01656a 100644 --- a/puzzles files/nurikabe/7x7 Nurikabe Normal/8786625 +++ b/puzzles files/nurikabe/7x7 Nurikabe Normal/8786625 @@ -12,5 +12,4 @@ - diff --git a/puzzles files/shorttruthtable/DeMorgan.xml b/puzzles files/shorttruthtable/DeMorgan.xml index 70b806068..d6acef677 100644 --- a/puzzles files/shorttruthtable/DeMorgan.xml +++ b/puzzles files/shorttruthtable/DeMorgan.xml @@ -8,5 +8,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_01.xml b/puzzles files/shorttruthtable/Heuveln_01.xml index be62afcf9..b71e69f02 100644 --- a/puzzles files/shorttruthtable/Heuveln_01.xml +++ b/puzzles files/shorttruthtable/Heuveln_01.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_02.xml b/puzzles files/shorttruthtable/Heuveln_02.xml index dde0fa162..8446e568f 100644 --- a/puzzles files/shorttruthtable/Heuveln_02.xml +++ b/puzzles files/shorttruthtable/Heuveln_02.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_03.xml b/puzzles files/shorttruthtable/Heuveln_03.xml index 6f1fbaebc..06259e63d 100644 --- a/puzzles files/shorttruthtable/Heuveln_03.xml +++ b/puzzles files/shorttruthtable/Heuveln_03.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_04.xml b/puzzles files/shorttruthtable/Heuveln_04.xml index fc7ad31ad..476115bda 100644 --- a/puzzles files/shorttruthtable/Heuveln_04.xml +++ b/puzzles files/shorttruthtable/Heuveln_04.xml @@ -9,5 +9,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_04.xml_test02 b/puzzles files/shorttruthtable/Heuveln_04.xml_test02 index bcfdbe2bd..917855ed3 100644 --- a/puzzles files/shorttruthtable/Heuveln_04.xml_test02 +++ b/puzzles files/shorttruthtable/Heuveln_04.xml_test02 @@ -1,27 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/shorttruthtable/Heuveln_04_export test.xml b/puzzles files/shorttruthtable/Heuveln_04_export test.xml index 5d9c3f2d1..3fd197411 100644 --- a/puzzles files/shorttruthtable/Heuveln_04_export test.xml +++ b/puzzles files/shorttruthtable/Heuveln_04_export test.xml @@ -65,5 +65,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_04_test.xml b/puzzles files/shorttruthtable/Heuveln_04_test.xml index 2678f6b3a..97809fa02 100644 --- a/puzzles files/shorttruthtable/Heuveln_04_test.xml +++ b/puzzles files/shorttruthtable/Heuveln_04_test.xml @@ -23,5 +23,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_04_test01.xml b/puzzles files/shorttruthtable/Heuveln_04_test01.xml index fa5326e40..967710d9d 100644 --- a/puzzles files/shorttruthtable/Heuveln_04_test01.xml +++ b/puzzles files/shorttruthtable/Heuveln_04_test01.xml @@ -23,5 +23,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_04_test03.xml b/puzzles files/shorttruthtable/Heuveln_04_test03.xml index 441b16a3a..6120636f4 100644 --- a/puzzles files/shorttruthtable/Heuveln_04_test03.xml +++ b/puzzles files/shorttruthtable/Heuveln_04_test03.xml @@ -23,5 +23,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_04_test_exprt.xml b/puzzles files/shorttruthtable/Heuveln_04_test_exprt.xml index 60583987b..a46806253 100644 --- a/puzzles files/shorttruthtable/Heuveln_04_test_exprt.xml +++ b/puzzles files/shorttruthtable/Heuveln_04_test_exprt.xml @@ -23,5 +23,4 @@ - diff --git a/puzzles files/shorttruthtable/Heuveln_05.xml b/puzzles files/shorttruthtable/Heuveln_05.xml index 5ade9817e..a93f5aec6 100644 --- a/puzzles files/shorttruthtable/Heuveln_05.xml +++ b/puzzles files/shorttruthtable/Heuveln_05.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/invalid1.xml b/puzzles files/shorttruthtable/invalid1.xml index e3be7fbb8..dc0f3723d 100644 --- a/puzzles files/shorttruthtable/invalid1.xml +++ b/puzzles files/shorttruthtable/invalid1.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/invalid2.xml b/puzzles files/shorttruthtable/invalid2.xml index 03a304db5..08cacd95b 100644 --- a/puzzles files/shorttruthtable/invalid2.xml +++ b/puzzles files/shorttruthtable/invalid2.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/invalid3.xml b/puzzles files/shorttruthtable/invalid3.xml index e5ad96525..2b352548d 100644 --- a/puzzles files/shorttruthtable/invalid3.xml +++ b/puzzles files/shorttruthtable/invalid3.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/shorttruthtable/test.xml b/puzzles files/shorttruthtable/test.xml index 06298261c..78326985c 100644 --- a/puzzles files/shorttruthtable/test.xml +++ b/puzzles files/shorttruthtable/test.xml @@ -10,5 +10,4 @@ - diff --git a/puzzles files/skyscrapers/1646651 b/puzzles files/skyscrapers/1646651 index 9e2536931..3facc12fe 100644 --- a/puzzles files/skyscrapers/1646651 +++ b/puzzles files/skyscrapers/1646651 @@ -27,5 +27,4 @@ - diff --git a/puzzles files/skyscrapers/4x4 Skyscrapers Easy1 b/puzzles files/skyscrapers/4x4 Skyscrapers Easy1 deleted file mode 100644 index 5b91e64ca..000000000 --- a/puzzles files/skyscrapers/4x4 Skyscrapers Easy1 +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/4x4 Skyscrapers Easy3 b/puzzles files/skyscrapers/4x4 Skyscrapers Easy3 deleted file mode 100644 index 61fa8054b..000000000 --- a/puzzles files/skyscrapers/4x4 Skyscrapers Easy3 +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 b/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 deleted file mode 100644 index 111d2bd87..000000000 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Easy2 b/puzzles files/skyscrapers/5x5 Skyscrapers Easy2 deleted file mode 100644 index 45a1db188..000000000 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Easy2 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Easy3 b/puzzles files/skyscrapers/5x5 Skyscrapers Easy3 deleted file mode 100644 index 87f7db3d7..000000000 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Easy3 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Medium1 b/puzzles files/skyscrapers/5x5 Skyscrapers Medium1 deleted file mode 100644 index 9e79c6248..000000000 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Medium1 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Medium2 b/puzzles files/skyscrapers/5x5 Skyscrapers Medium2 deleted file mode 100644 index fab87f9f7..000000000 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Medium2 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Medium3 b/puzzles files/skyscrapers/5x5 Skyscrapers Medium3 deleted file mode 100644 index efd123165..000000000 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Medium3 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/4x4 Skyscrapers Easy2 b/puzzles files/skyscrapers/easy similarity index 77% rename from puzzles files/skyscrapers/4x4 Skyscrapers Easy2 rename to puzzles files/skyscrapers/easy index e30fbd89e..48c255771 100644 --- a/puzzles files/skyscrapers/4x4 Skyscrapers Easy2 +++ b/puzzles files/skyscrapers/easy @@ -1,23 +1,22 @@ - + - - + + - + + - - diff --git a/puzzles files/skyscrapers/easy1.xml b/puzzles files/skyscrapers/easy1.xml index ce09cdf7b..9233705e9 100644 --- a/puzzles files/skyscrapers/easy1.xml +++ b/puzzles files/skyscrapers/easy1.xml @@ -34,7 +34,6 @@ - + + + + + ${org.eclipse.m2e.log.dir}/0.log + + ${org.eclipse.m2e.log.dir}/%i.log + 1 + 10 + + + 100MB + + + %date [%thread] %-5level %logger{35} - %msg%n + + + + + + WARN + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup new file mode 100644 index 000000000..1f73e14c1 --- /dev/null +++ b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup @@ -0,0 +1,6 @@ + + diff --git a/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml new file mode 100644 index 000000000..c00d6ca9e --- /dev/null +++ b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml @@ -0,0 +1,11 @@ + +

+
+ + +
+
+ + +
+
diff --git a/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml new file mode 100644 index 000000000..541da6a75 --- /dev/null +++ b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.intro/dialog_settings.xml @@ -0,0 +1,4 @@ + +
+ +
diff --git a/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml new file mode 100644 index 000000000..01f7412af --- /dev/null +++ b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml @@ -0,0 +1,15 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml new file mode 100644 index 000000000..ef53d8bf2 --- /dev/null +++ b/src/main/java/edu/rpi/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/.metadata/version.ini b/src/main/java/edu/rpi/.metadata/version.ini new file mode 100644 index 000000000..a612e14df --- /dev/null +++ b/src/main/java/edu/rpi/.metadata/version.ini @@ -0,0 +1,3 @@ +#Wed Nov 06 12:29:47 EST 2019 +org.eclipse.core.runtime=2 +org.eclipse.platform=4.5.2.v20160212-1500 diff --git a/src/main/java/edu/rpi/legup/.metadata/.lock b/src/main/java/edu/rpi/legup/.metadata/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/edu/rpi/legup/.metadata/.mylyn/.taskListIndex/segments.gen b/src/main/java/edu/rpi/legup/.metadata/.mylyn/.taskListIndex/segments.gen new file mode 100644 index 0000000000000000000000000000000000000000..63a7ec9a3ce3e4c844ffb7c8dd88e6eb3ff32ef5 GIT binary patch literal 20 QcmezW|NlP*2w;TK07=6G{r~^~ literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.mylyn/.taskListIndex/segments_1 b/src/main/java/edu/rpi/legup/.metadata/.mylyn/.taskListIndex/segments_1 new file mode 100644 index 0000000000000000000000000000000000000000..adecb06199ff8e2649f8a0c89bcb7b1d747b3395 GIT binary patch literal 32 acmezW|NmD82FBcFG|--P0qyJm CSO?Dl literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources new file mode 100644 index 0000000000000000000000000000000000000000..f063e7cafc20c0a3642b699c3ea7eed318d5b30c GIT binary patch literal 436 zcmZ?R*xjhShe1S2b=vdAllRFf=Oz}Hq!uZZBqrsgaw&(DrYiU+mnaxnDi|4A8CqDG zS}3@>geVvp7+P|rq~??)x>giq7A2Ns=I6!d7p3c^Cg)@p6sPKCrIhF;=NF~g8k(9L znE+KATbNi{awX@aCKkDX^kG+>mY7qVic3{)Wlm+DUP)qccClV*rY%IRzFtXDYO1Xf X*MW%NpTRx}{M=K$yZo5Lu=N=LbmN;7 literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..dffc6b513 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +version=1 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..1768a1e88 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,13 @@ +content_assist_proposals_background=255,255,255 +content_assist_proposals_foreground=0,0,0 +eclipse.preferences.version=1 +fontPropagated=true +org.eclipse.jdt.ui.editor.tab.width= +org.eclipse.jdt.ui.formatterprofiles.version=12 +org.eclipse.jdt.ui.javadoclocations.migrated=true +org.eclipse.jface.textfont=1|Monaco|11.0|0|COCOA|1|; +proposalOrderMigrated=true +spelling_locale_initialized=true +tabWidthPropagated=true +useAnnotationsPrefPage=true +useQuickDiffPrefPage=true diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs new file mode 100644 index 000000000..67b1d96c9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.m2e.discovery.pref.projects= diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs new file mode 100644 index 000000000..43e97e405 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +mylyn.attention.migrated=true diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs new file mode 100644 index 000000000..8d462a6cf --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.mylyn.monitor.activity.tracking.enabled.checked=true diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 000000000..2b60c21d6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +migrated.task.repositories.secure.store=true +org.eclipse.mylyn.tasks.ui.filters.nonmatching=true +org.eclipse.mylyn.tasks.ui.filters.nonmatching.encouraged=true +org.eclipse.mylyn.tasks.ui.welcome.message=true diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs new file mode 100644 index 000000000..da2be0834 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs @@ -0,0 +1,5 @@ +PROBLEMS_FILTERS_MIGRATE=true +eclipse.preferences.version=1 +platformState=1554955212164 +quickStart=false +tipsAndTricks=true diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs new file mode 100644 index 000000000..08076f236 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +showIntro=false diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs new file mode 100644 index 000000000..dd774965c --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs @@ -0,0 +1,3 @@ +//org.eclipse.ui.commands/state/org.eclipse.ui.navigator.resources.nested.changeProjectPresentation/org.eclipse.ui.commands.radioState=false +PLUGINS_NOT_ACTIVATED_ON_STARTUP=org.eclipse.m2e.discovery; +eclipse.preferences.version=1 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi new file mode 100644 index 000000000..51f42e4ed --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi @@ -0,0 +1,2308 @@ + + + + activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration + ModelMigrationProcessor.001 + + + + + + + + topLevel + + + Minimized + MinimizedByZoom + + + persp.actionSet:org.eclipse.mylyn.doc.actionSet + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation + persp.actionSet:org.eclipse.ui.cheatsheets.actionSet + persp.actionSet:org.eclipse.search.searchActionSet + persp.actionSet:org.eclipse.ui.edit.text.actionSet.annotationNavigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.navigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo + persp.actionSet:org.eclipse.ui.externaltools.ExternalToolsSet + persp.actionSet:org.eclipse.ui.actionSet.keyBindings + persp.actionSet:org.eclipse.ui.actionSet.openFiles + persp.actionSet:org.eclipse.wb.core.ui.actionset + persp.actionSet:org.eclipse.debug.ui.launchActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaElementCreationActionSet + persp.actionSet:org.eclipse.ui.NavigateActionSet + persp.viewSC:org.eclipse.jdt.ui.PackageExplorer + persp.viewSC:org.eclipse.jdt.ui.TypeHierarchy + persp.viewSC:org.eclipse.jdt.ui.SourceView + persp.viewSC:org.eclipse.jdt.ui.JavadocView + persp.viewSC:org.eclipse.search.ui.views.SearchView + persp.viewSC:org.eclipse.ui.console.ConsoleView + persp.viewSC:org.eclipse.ui.views.ContentOutline + persp.viewSC:org.eclipse.ui.views.ProblemView + persp.viewSC:org.eclipse.ui.views.ResourceNavigator + persp.viewSC:org.eclipse.ui.views.TaskList + persp.viewSC:org.eclipse.ui.views.ProgressView + persp.viewSC:org.eclipse.ui.navigator.ProjectExplorer + persp.viewSC:org.eclipse.ui.texteditor.TemplatesView + persp.viewSC:org.eclipse.pde.runtime.LogView + persp.newWizSC:org.eclipse.jdt.ui.wizards.JavaProjectWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewPackageCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewClassCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewEnumCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewAnnotationCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewSourceFolderCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewSnippetFileCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewJavaWorkingSetWizard + persp.newWizSC:org.eclipse.ui.wizards.new.folder + persp.newWizSC:org.eclipse.ui.wizards.new.file + persp.newWizSC:org.eclipse.ui.editors.wizards.UntitledTextFileWizard + persp.perspSC:org.eclipse.jdt.ui.JavaBrowsingPerspective + persp.perspSC:org.eclipse.debug.ui.DebugPerspective + persp.perspSC:com.android.ide.eclipse.ddms.Perspective + persp.viewSC:org.eclipse.ant.ui.views.AntView + persp.actionSet:org.eclipse.eclemma.ui.CoverageActionSet + persp.showIn:org.eclipse.eclemma.ui.CoverageView + persp.showIn:org.eclipse.egit.ui.RepositoriesView + persp.actionSet:org.eclipse.debug.ui.breakpointActionSet + persp.actionSet:org.eclipse.jdt.debug.ui.JDTDebugActionSet + persp.newWizSC:org.eclipse.jdt.junit.wizards.NewTestCaseCreationWizard + persp.actionSet:org.eclipse.jdt.junit.JUnitActionSet + persp.showIn:org.eclipse.jdt.ui.PackageExplorer + persp.showIn:org.eclipse.team.ui.GenericHistoryView + persp.showIn:org.eclipse.ui.views.ResourceNavigator + persp.showIn:org.eclipse.ui.navigator.ProjectExplorer + persp.viewSC:org.eclipse.mylyn.tasks.ui.views.tasks + persp.newWizSC:org.eclipse.mylyn.tasks.ui.wizards.new.repository.task + persp.viewSC:org.eclipse.wb.core.StructureView + persp.viewSC:org.eclipse.wb.core.PaletteView + + + + org.eclipse.e4.primaryNavigationStack + + + + + + + + + + + + + + + + + + + + + org.eclipse.e4.secondaryNavigationStack + + + + + + + + org.eclipse.e4.secondaryDataStack + + + + + + + + + + + + + + active + Maximized + + + + + + + View + categoryTag:Help + + + + View + categoryTag:General + active + activeOnClose + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:Help + + + + org.eclipse.e4.primaryDataStack + EditorStack + + + + + View + categoryTag:Java + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:Java + + + View + categoryTag:General + + + View + categoryTag:General + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:General + + + View + categoryTag:Ant + + + View + categoryTag:Git + + + View + categoryTag:Java + + + + View + categoryTag:Mylyn + + ViewMenu + menuContribution:menu + + + + + View + categoryTag:WindowBuilder + + + View + categoryTag:WindowBuilder + + + + toolbarSeparator + + + + Draggable + + + + toolbarSeparator + + + + Draggable + + + Draggable + + + Draggable + + + toolbarSeparator + + + + Draggable + + + + toolbarSeparator + + + + toolbarSeparator + + + + Draggable + + + stretch + SHOW_RESTORE_MENU + + + Draggable + HIDEABLE + SHOW_RESTORE_MENU + + + + + stretch + + + Draggable + + + Draggable + + + + + + TrimStack + + + + + + + + + + + + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + + platform:cocoa + + + + + platform:cocoa + + + + + platform:cocoa + + + + + platform:cocoa + + + + + + + + + + + + + + + platform:cocoa + + + + + platform:cocoa + + + + + + platform:cocoa + + + + + platform:cocoa + + + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + + + + + platform:cocoa + + + + + platform:cocoa + + + + + + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + + + + + + + + + platform:cocoa + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + + + platform:cocoa + + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + + + + + platform:cocoa + + + + + + platform:cocoa + + + + platform:cocoa + + + + + + + + + + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + + + + platform:cocoa + + + + + + + + + + + + + platform:cocoa + + + + + + + + platform:cocoa + + + + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + + + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + + + + platform:cocoa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + platform:cocoa + + + + platform:cocoa + + + + + + + platform:cocoa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Android + + + View + categoryTag:Tracer for OpenGL ES + + + View + categoryTag:Tracer for OpenGL ES + + + View + categoryTag:Tracer for OpenGL ES + + + View + categoryTag:Ant + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + View + categoryTag:Java + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:Git + + + View + categoryTag:General + + + View + categoryTag:Help + + + View + categoryTag:Debug + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java Browsing + + + View + categoryTag:Java + + + View + categoryTag:General + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:Maven + + + View + categoryTag:Maven + + + View + categoryTag:Mylyn + + + View + categoryTag:Mylyn + + + View + categoryTag:Mylyn + + + View + categoryTag:Mylyn + + + View + categoryTag:Oomph + + + View + categoryTag:Code Recommenders + + + View + categoryTag:Code Recommenders + + + View + categoryTag:Code Recommenders + + + View + categoryTag:Code Recommenders + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:Team + + + View + categoryTag:Team + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:Help + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:WindowBuilder + + + View + categoryTag:WindowBuilder + + + View + categoryTag:General + + + View + categoryTag:XML + + + View + categoryTag:XML + + + + glue + move_after:PerspectiveSpacer + SHOW_RESTORE_MENU + + + move_after:Spacer Glue + HIDEABLE + SHOW_RESTORE_MENU + + + glue + move_after:SearchField + SHOW_RESTORE_MENU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdt b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdt new file mode 100644 index 0000000000000000000000000000000000000000..d5abf41cc49dfca7560ba7bda088ecb22c377ca6 GIT binary patch literal 11 QcmZQzU|?nhVgo%h00CG4oB#j- literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdx b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fdx new file mode 100644 index 0000000000000000000000000000000000000000..b8ee80957685785cf4fa43c7adecee1e634fd737 GIT binary patch literal 12 OcmZQzU|?o|02TlMD*y-p literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fnm b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fnm new file mode 100644 index 000000000..523c92e25 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.fnm @@ -0,0 +1 @@ +ýÿÿÿversion \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.frq b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.frq new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.nrm b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.nrm new file mode 100644 index 000000000..cf8dc7529 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.nrm @@ -0,0 +1 @@ +NRMÿ \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tii b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tii new file mode 100644 index 0000000000000000000000000000000000000000..ebd518d6e4ff5decae514a3f6233ba3198b30ae3 GIT binary patch literal 24 UcmezW|NkEb1ZV&<1%Q|f0A5)HlmGw# literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tis b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/_0.tis new file mode 100644 index 0000000000000000000000000000000000000000..ebd518d6e4ff5decae514a3f6233ba3198b30ae3 GIT binary patch literal 24 UcmezW|NkEb1ZV&<1%Q|f0A5)HlmGw# literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments.gen b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments.gen new file mode 100644 index 0000000000000000000000000000000000000000..63a7ec9a3ce3e4c844ffb7c8dd88e6eb3ff32ef5 GIT binary patch literal 20 QcmezW|NlP*2w;TK07=6G{r~^~ literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments_1 b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/history/segments_1 new file mode 100644 index 0000000000000000000000000000000000000000..4f495c834e1a2cd7916f279dcc6d6d4d60b20827 GIT binary patch literal 243 zcmYjLJqyAx5Uq->UpP29W*0-Q#u8nfrHG4L2{B@&Z6vWp|CT?+#b4s!RAW`}amT%P zkNYUg^5HnO^m)6i982EWnBZwWB5zv$;UjFff>&3jwd4YYYqaSec)@WYv#rvDP;-$; zv{3q}G(W{E>wRr)tY`yfm_#svev%8BNjp~=BYPGT1U{iN#?Tmip@O=by sPJ{UrJY_J~V(*?=5CzmVD%V0bcS&yc9qWBpdO$Ex9Q>Yk-1~L-0*0qf4gdfE literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdt b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdt new file mode 100644 index 0000000000000000000000000000000000000000..d5abf41cc49dfca7560ba7bda088ecb22c377ca6 GIT binary patch literal 11 QcmZQzU|?nhVgo%h00CG4oB#j- literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdx b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fdx new file mode 100644 index 0000000000000000000000000000000000000000..b8ee80957685785cf4fa43c7adecee1e634fd737 GIT binary patch literal 12 OcmZQzU|?o|02TlMD*y-p literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fnm b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fnm new file mode 100644 index 000000000..523c92e25 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.fnm @@ -0,0 +1 @@ +ýÿÿÿversion \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.frq b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.frq new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.nrm b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.nrm new file mode 100644 index 000000000..cf8dc7529 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.nrm @@ -0,0 +1 @@ +NRMÿ \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tii b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tii new file mode 100644 index 0000000000000000000000000000000000000000..ebd518d6e4ff5decae514a3f6233ba3198b30ae3 GIT binary patch literal 24 UcmezW|NkEb1ZV&<1%Q|f0A5)HlmGw# literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tis b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/_0.tis new file mode 100644 index 0000000000000000000000000000000000000000..ebd518d6e4ff5decae514a3f6233ba3198b30ae3 GIT binary patch literal 24 UcmezW|NkEb1ZV&<1%Q|f0A5)HlmGw# literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments.gen b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments.gen new file mode 100644 index 0000000000000000000000000000000000000000..63a7ec9a3ce3e4c844ffb7c8dd88e6eb3ff32ef5 GIT binary patch literal 20 QcmezW|NlP*2w;TK07=6G{r~^~ literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments_1 b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.epp.logging.aeri.ui/remote-index/segments_1 new file mode 100644 index 0000000000000000000000000000000000000000..4f495c834e1a2cd7916f279dcc6d6d4d60b20827 GIT binary patch literal 243 zcmYjLJqyAx5Uq->UpP29W*0-Q#u8nfrHG4L2{B@&Z6vWp|CT?+#b4s!RAW`}amT%P zkNYUg^5HnO^m)6i982EWnBZwWB5zv$;UjFff>&3jwd4YYYqaSec)@WYv#rvDP;-$; zv{3q}G(W{E>wRr)tY`yfm_#svev%8BNjp~=BYPGT1U{iN#?Tmip@O=by sPJ{UrJY_J~V(*?=5CzmVD%V0bcS&yc9qWBpdO$Ex9Q>Yk-1~L-0*0qf4gdfE literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat new file mode 100644 index 0000000000000000000000000000000000000000..0edae4b20855dcd5c83bdac184b9ed16afb1b634 GIT binary patch literal 110 zcmZQzU|?c^05&ki?iJ)3@8jvj2;?y`aD#ZkLC!(`{vjX{CI&9AP(RO*cn^PHSC9ZR e16Tu435dtSzz2~A^5IHY8Q6V|;)7fR{22i=Q4xRu literal 0 HcmV?d00001 diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml new file mode 100644 index 000000000..a4ee3cbc9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml @@ -0,0 +1,2 @@ + + diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml new file mode 100644 index 000000000..9e390f501 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml @@ -0,0 +1,2 @@ + + diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml new file mode 100644 index 000000000..0bf6a8c76 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -0,0 +1,11 @@ + +
+
+ + + + + +
+
diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml new file mode 100644 index 000000000..63d411340 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.6.0.20150526-2032.xml @@ -0,0 +1,43 @@ + + + + %date [%thread] %-5level %logger{35} - %msg%n + + + OFF + + + + + ${org.eclipse.m2e.log.dir}/0.log + + ${org.eclipse.m2e.log.dir}/%i.log + 1 + 10 + + + 100MB + + + %date [%thread] %-5level %logger{35} - %msg%n + + + + + + WARN + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup new file mode 100644 index 000000000..1f73e14c1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup @@ -0,0 +1,6 @@ + + diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml new file mode 100644 index 000000000..c00d6ca9e --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml @@ -0,0 +1,11 @@ + +
+
+ + +
+
+ + +
+
diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml new file mode 100644 index 000000000..d2ff1eed0 --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml @@ -0,0 +1,15 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml new file mode 100644 index 000000000..5e61d80ea --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/.metadata/version.ini b/src/main/java/edu/rpi/legup/.metadata/version.ini new file mode 100644 index 000000000..026f9536e --- /dev/null +++ b/src/main/java/edu/rpi/legup/.metadata/version.ini @@ -0,0 +1,3 @@ +#Tue Oct 29 22:17:24 EDT 2019 +org.eclipse.core.runtime=2 +org.eclipse.platform=4.5.2.v20160212-1500 diff --git a/src/main/java/edu/rpi/legup/controller/EditorElementController.java b/src/main/java/edu/rpi/legup/controller/EditorElementController.java index 80e7dd35a..211012c9f 100644 --- a/src/main/java/edu/rpi/legup/controller/EditorElementController.java +++ b/src/main/java/edu/rpi/legup/controller/EditorElementController.java @@ -5,9 +5,7 @@ import edu.rpi.legup.history.*; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.elements.Element; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.*; import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.model.tree.TreeElementType; @@ -19,8 +17,6 @@ import edu.rpi.legup.ui.puzzleeditorui.elementsview.ElementButton; import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; @@ -30,12 +26,10 @@ public class EditorElementController implements ActionListener { protected Object lastSource; protected ElementController elementController; - protected ElementButton prevButton; public EditorElementController() { super(); elementController = null; - prevButton = null; } public void setElementController(ElementController elementController) { @@ -44,7 +38,6 @@ public void setElementController(ElementController elementController) { public void buttonPressed(Element element) { // TODO: implement what happens when element is pressed - System.out.printf("%s button pressed!\n", element.getElementName()); if (elementController != null) { elementController.setSelectedElement(element); @@ -56,16 +49,5 @@ public void actionPerformed(ActionEvent e) { lastSource = e.getSource(); ElementButton button = (ElementButton) lastSource; buttonPressed(button.getElement()); - - // reset border in previous selected button - if (this.prevButton != null) { - this.prevButton.resetBorder(); - } - - // change border color when select a button - button.setBorderToSelected(); - - this.prevButton = button; - } } diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index f6f67c8d7..ae5f62f22 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -105,10 +105,6 @@ public String getErrorString() { return "The selected data element is not pickable with this case rule."; } - if(caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() == 0){ - return "The selection must produce at least one case"; - } - return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightBasicRule.java index 92d080cf8..2830a497a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightBasicRule.java @@ -38,8 +38,9 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem return super.getInvalidUseOfRuleMessage() + ": Modified cells must be bulbs"; } + finalCell.setData(LightUpCellType.EMPTY.value); finalBoard.fillWithLight(); - boolean isForced = isForcedBulb(parentBoard, finalCell.getLocation()); + boolean isForced = isForcedBulb(finalBoard, finalCell.getLocation()); finalCell.setData(LightUpCellType.BULB.value); finalBoard.fillWithLight(); @@ -52,13 +53,12 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } private boolean isForcedBulb(LightUpBoard board, Point loc) { + CannotLightACellContradictionRule cannotLite = new CannotLightACellContradictionRule(); LightUpCell cell = board.getCell(loc.x, loc.y); - //Check if this cell itself (the one with the bulb) has no other lighting option if ((cell.getType() == LightUpCellType.EMPTY || cell.getType() == LightUpCellType.UNKNOWN) && - !cell.isLite() && countPossibleBulbs(board, cell) == 0) { + !cell.isLite() && cannotLite.checkContradictionAt(board, cell) == null) { return true; } - //Look right for (int i = loc.x + 1; i < board.getWidth(); i++) { LightUpCell c = board.getCell(i, loc.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { @@ -66,12 +66,11 @@ private boolean isForcedBulb(LightUpBoard board, Point loc) { } else { if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && countPossibleBulbs(board, c) == 1) { + !c.isLite() && cannotLite.checkContradictionAt(board, c) == null) { return true; } } } - //Look left for (int i = loc.x - 1; i >= 0; i--) { LightUpCell c = board.getCell(i, loc.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { @@ -79,12 +78,11 @@ private boolean isForcedBulb(LightUpBoard board, Point loc) { } else { if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && countPossibleBulbs(board, c) == 1) { + !c.isLite() && cannotLite.checkContradictionAt(board, c) == null) { return true; } } } - //Look down for (int i = loc.y + 1; i < board.getHeight(); i++) { LightUpCell c = board.getCell(loc.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { @@ -92,12 +90,11 @@ private boolean isForcedBulb(LightUpBoard board, Point loc) { } else { if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && countPossibleBulbs(board, c) == 1) { + !c.isLite() && cannotLite.checkContradictionAt(board, c) == null) { return true; } } } - //Look up for (int i = loc.y - 1; i >= 0; i--) { LightUpCell c = board.getCell(loc.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { @@ -105,7 +102,7 @@ private boolean isForcedBulb(LightUpBoard board, Point loc) { } else { if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && countPossibleBulbs(board, c) == 1) { + !c.isLite() && cannotLite.checkContradictionAt(board, c) == null) { return true; } } @@ -113,72 +110,6 @@ private boolean isForcedBulb(LightUpBoard board, Point loc) { return false; } - /** - * Checks the number of cells that can contain a bulb that lights the given cell - * - * @param board transition to check - * @param cell index of the puzzleElement - * @return -1 if the cell is already lit, - * otherwise the number of that can contain a bulb that lights the given cell - */ - public int countPossibleBulbs(LightUpBoard board, LightUpCell cell) { - if (cell.isLite()) { - return -1; - } - Point location = cell.getLocation(); - int ver_count = 0; - int hor_count = 0; - //Look right - for (int i = location.x + 1; i < board.getWidth(); i++) { - LightUpCell c = board.getCell(i, location.y); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { - hor_count += 1; - } - } - } - //Look left - for (int i = location.x - 1; i >= 0; i--) { - LightUpCell c = board.getCell(i, location.y); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { - hor_count += 1; - } - } - } - //Look down - for (int i = location.y + 1; i < board.getHeight(); i++) { - LightUpCell c = board.getCell(location.x, i); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { - ver_count += 1; - } - } - } - //Look up - for (int i = location.y - 1; i >= 0; i--) { - LightUpCell c = board.getCell(location.x, i); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { - ver_count += 1; - } - } - } - return hor_count + ver_count; - } - /** * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. * diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java index 1510a3124..b0a49d338 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java @@ -18,8 +18,6 @@ public MasyuType getType() { return MasyuType.BLACK; case 2: return MasyuType.WHITE; - case 3: - return MasyuType.LINE; default: return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java index 67894f368..72c5e634a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java @@ -80,22 +80,8 @@ public void mouseReleased(MouseEvent e) { masyuLine.clear(); } - /** - * Alters the cells as they are being dragged over or clicked - * @param e Mouse event being used - * @param data Data of selected cell - */ @Override public void changeCell(MouseEvent e, PuzzleElement data) { - MasyuCell cell = (MasyuCell) data; - if(cell.getData() == 1 || cell.getData() == 2) { - return; - } - if(cell.getData() == 0) { - data.setData(3); - } - else { - data.setData(0); - } + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java index fa6abdfb6..9b23ed285 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java @@ -1,5 +1,5 @@ package edu.rpi.legup.puzzle.masyu; public enum MasyuType { - UNKNOWN, BLACK, WHITE, LINE + UNKNOWN, BLACK, WHITE } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java index d3fea6a21..f8125ad43 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java @@ -4,9 +4,8 @@ import edu.rpi.legup.utility.DisjointSets; import java.awt.*; -import java.util.HashMap; +import java.util.ArrayList; import java.util.HashSet; -import java.util.LinkedList; import java.util.Set; public class NurikabeUtilities { @@ -166,79 +165,72 @@ public static DisjointSets getPossibleWhiteRegions(NurikabeBoard b } /** - * Makes a map where the keys are white/numbered cells - * and the values are the amount of cells that need - * to be added to the region + * Gets a list of flood filled white regions with remaining white cells * * @param board nurikabe board - * @return a map of cell keys to integer values + * @return a list of flood filled white regions */ - public static HashMap getWhiteRegionMap(NurikabeBoard board) { + public static ArrayList> getFloodFillWhite(NurikabeBoard board) { int width = board.getWidth(); int height = board.getHeight(); - Set numberedCells = getNurikabeNumberedCells(board); - // Final mapping of cell to size - HashMap whiteRegionMap = new HashMap<>(); - for (NurikabeCell center: numberedCells) { - //BFS for each center to find the size of the region - int size = 1; - // Mark all the vertices as not visited(By default - // set as false) - HashMap visited= new HashMap<>(); - - // Create a queue for BFS - LinkedList queue = new LinkedList<>(); - - // Mark the current node as visited and enqueue it - visited.put(center,true); - queue.add(center); - - // Set of cells in the current region - Set connected = new HashSet<>(); - - while (queue.size() != 0) { - // Dequeue a vertex from queue and print it - // s is the source node in the graph - NurikabeCell s = queue.poll(); - System.out.print(s+" "); - - // Make a linked list of all adjacent squares - Set adj = new HashSet<>(); - - Point loc = s.getLocation(); - // First check if the side is on the board - if (loc.x >= 1) { - adj.add(board.getCell(loc.x-1, loc.y)); - } - if (loc.x < width-1) { - adj.add(board.getCell(loc.x+1, loc.y)); - } - if (loc.y >= 1) { - adj.add(board.getCell(loc.x, loc.y-1)); - } - if (loc.y < height-1) { - adj.add(board.getCell(loc.x, loc.y+1)); - } - // Get all adjacent vertices of the dequeued vertex s - // If a adjacent has not been visited, then mark it - // visited and enqueue it - for (NurikabeCell n : adj) { - if (!visited.getOrDefault(n,false) - && n.getType() == NurikabeType.WHITE) { - connected.add(n); - visited.put(n,true); - queue.add(n); - ++size; + DisjointSets whiteRegions = new DisjointSets<>(); + for (PuzzleElement data : board.getPuzzleElements()) { + NurikabeCell cell = (NurikabeCell) data; + whiteRegions.createSet(cell); + } + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + NurikabeCell cell = board.getCell(x, y); + NurikabeCell rightCell = board.getCell(x + 1, y); + NurikabeCell downCell = board.getCell(x, y + 1); + if (cell.getType() == NurikabeType.WHITE || cell.getType() == NurikabeType.NUMBER) { + if (rightCell != null && (rightCell.getType() == NurikabeType.WHITE || + rightCell.getType() == NurikabeType.NUMBER)) { + whiteRegions.union(cell, rightCell); + } + if (downCell != null && (downCell.getType() == NurikabeType.WHITE || + downCell.getType() == NurikabeType.NUMBER)) { + whiteRegions.union(cell, downCell); } } } - // Map the cells to the center-size (including the center) - whiteRegionMap.put(center,center.getData()-size); - for (NurikabeCell member : connected) { - whiteRegionMap.put(member,center.getData()-size); + } + + Set numberedCells = getNurikabeNumberedCells(board); + ArrayList> floodFilledRegions = new ArrayList<>(); + for (NurikabeCell numberCell : numberedCells) { + int number = numberCell.getData(); + Set region = whiteRegions.getSet(numberCell); + floodFilledRegions.add(region); + + int flood = number - region.size(); + for (int i = 0; i < flood; i++) { + Set newSet = new HashSet<>(); + for (NurikabeCell c : region) { + Point loc = c.getLocation(); + NurikabeCell upCell = board.getCell(loc.x, loc.y - 1); + NurikabeCell rightCell = board.getCell(loc.x + 1, loc.y); + NurikabeCell downCell = board.getCell(loc.x, loc.y + 1); + NurikabeCell leftCell = board.getCell(loc.x - 1, loc.y); + if (upCell != null) { + newSet.add(upCell); + } + if (rightCell != null) { + newSet.add(rightCell); + } + if (downCell != null) { + newSet.add(downCell); + } + if (leftCell != null) { + newSet.add(leftCell); + } + } + region.addAll(newSet); } } - return whiteRegionMap; + + return floodFilledRegions; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckBasicRule.java index a483236b8..9df8ac70b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckBasicRule.java @@ -15,7 +15,7 @@ public class BlackBottleNeckBasicRule extends BasicRule { public BlackBottleNeckBasicRule() { super("NURI-BASC-0002", "Black Bottle Neck", - "If there is only one path for a black to escape, then those unknowns must be black.", + "If there is only one path for a black to escape, then those unknowns must be white.", "edu/rpi/legup/images/nurikabe/rules/OneUnknownBlack.png"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java index 4076ff9bd..7b4deb4ae 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellBasicRule.java @@ -40,12 +40,20 @@ protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleE NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); NurikabeBoard modified = origBoardState.copy(); + for (int i = 0; i < modified.getWidth(); i++) { + for (int j = 0; j < modified.getHeight(); j++) { + NurikabeCell currentCell = modified.getCell(i, j); + if (currentCell.getType() == NurikabeType.WHITE) { + currentCell.setData(NurikabeType.UNKNOWN.toValue()); + } + } + } NurikabeCell modifiedCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); modifiedCell.setData(NurikabeType.WHITE.toValue()); - if (contraRule.checkContradictionAt(modified,modifiedCell) == null) { + if (contraRule.checkContradiction(modified) == null) { return null; } - return super.getInvalidUseOfRuleMessage() + ": Cell at this index can be reached"; + return super.getInvalidUseOfRuleMessage() + ": This is not the only way for black to escape!"; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java index cdad16bcb..f12bdb57f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java @@ -8,8 +8,8 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; -import java.awt.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Set; public class UnreachableWhiteCellContradictionRule extends ContradictionRule { @@ -40,63 +40,14 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } - int height = nurikabeBoard.getHeight(); - int width = nurikabeBoard.getWidth(); + ArrayList> regions = NurikabeUtilities.getFloodFillWhite(nurikabeBoard); - // Get regions - HashMap whiteRegionMap = NurikabeUtilities.getWhiteRegionMap(nurikabeBoard); - if (whiteRegionMap.containsKey(cell)) { - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; - } - // BFS to a region - - // Create a queue for BFS - LinkedList queue = new LinkedList<>(); - - // Mark the current node as visited and enqueue it - HashMap visited= new HashMap<>(); - visited.put(cell,true); - queue.add(cell); - int pathLength = 1; - while (queue.size() != 0) { - // Set of adjacent squares - Set adj = new HashSet<>(); - while (queue.size() != 0) { - // Dequeue a vertex from queue and print it - NurikabeCell s = queue.poll(); - - Point loc = s.getLocation(); - // First check if the side is on the board - if (loc.x >= 1) { - adj.add(nurikabeBoard.getCell(loc.x-1, loc.y)); - } - if (loc.x < width-1) { - adj.add(nurikabeBoard.getCell(loc.x+1, loc.y)); - } - if (loc.y >= 1) { - adj.add(nurikabeBoard.getCell(loc.x, loc.y-1)); - } - if (loc.y < height-1) { - adj.add(nurikabeBoard.getCell(loc.x, loc.y+1)); - } - - for (NurikabeCell n :adj) { - int regionNeed = whiteRegionMap.getOrDefault(n,-1); - if (pathLength <= regionNeed) { - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; - } - } - } - - for (NurikabeCell n : adj) { - if (!visited.getOrDefault(n,false) - && (n.getType() == NurikabeType.UNKNOWN || - n.getType() == NurikabeType.WHITE)) { - visited.put(n,true); - queue.add(n); + for (Set region : regions) { + for (NurikabeCell c : region) { + if (c == cell) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } } - ++pathLength; } return null; 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 71e8f8306..a4853993d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java @@ -4,6 +4,9 @@ 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.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.lightup.LightUpCellType; public class Skyscrapers extends Puzzle { @@ -23,6 +26,7 @@ public Skyscrapers() { */ @Override public void initializeView() { + SkyscrapersBoard board = (SkyscrapersBoard) currentBoard; boardView = new SkyscrapersView((SkyscrapersBoard) currentBoard); } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java index c23d2c46f..0e8786874 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -12,90 +12,57 @@ public class SkyscrapersBoard extends GridBoard { private ArrayList lines; + private ArrayList rowClues; + private ArrayList colClues; + private ArrayList row; + private ArrayList col; - private ArrayList eastClues; - //EAST clues - private ArrayList southClues; - //SOUTH clues - private ArrayList westClues; - //WEST clues - private ArrayList northClues; - //NORTH clues - - private boolean viewFlag = false; - private boolean dupeFlag = false; - - private SkyscrapersClue modClue = null; - //helper variable for case rule verification, tracks recently modified row/col - - public SkyscrapersBoard(int size) { - super(size, size); + public SkyscrapersBoard(int width, int height) { + super(width, height); this.lines = new ArrayList<>(); - this.eastClues = new ArrayList<>(); - this.southClues = new ArrayList<>(); - this.westClues = new ArrayList<>(); - this.northClues = new ArrayList<>(); + this.rowClues = new ArrayList<>(); + this.colClues = new ArrayList<>(); + this.row = new ArrayList<>(); + this.col = new ArrayList<>(); - for (int i = 0; i < size; i++) { - eastClues.add(null); - southClues.add(null); - westClues.add(null); - northClues.add(null); + for (int i = 0; i < height; i++) { + rowClues.add(null); + } + for (int i = 0; i < width; i++) { + colClues.add(null); + } + for (int i = 0; i < height; i++) { + row.add(null); + } + for (int i = 0; i < width; i++) { + col.add(null); } } - public ArrayList getLines() { - return lines; - } - - /** - * Returns a list of the eastern clues ordered from loc.y = 0->max - */ - public ArrayList getEastClues() { - return eastClues; + public SkyscrapersBoard(int size) { + this(size, size); } - /** - * Returns a list of the southern clues ordered from loc.x = 0->max - */ - public ArrayList getSouthClues() { - return southClues; + public ArrayList getLines() { + return lines; } - /** - * Returns a list of the western clues ordered from loc.y = 0->max - */ - public ArrayList getWestClues() { - return westClues; + public ArrayList getRowClues() { //EAST CLUE + return rowClues; } - /** - * Returns a list of the northern clues ordered from loc.x = 0->max - */ - public ArrayList getNorthClues() { - return northClues; + public ArrayList getColClues() { //SOUTH CLUE + return colClues; } - public boolean getDupeFlag(){ - return dupeFlag; - } - public boolean getViewFlag(){ - return viewFlag; - } - public void setDupeFlag(boolean newFlag){ - dupeFlag = newFlag; - } - public void setViewFlag(boolean newFlag){ - viewFlag = newFlag; + public ArrayList getRow() { //WEST CLUE + return row; } - public SkyscrapersClue getmodClue(){ - return modClue; - } - public void setModClue(SkyscrapersClue newClue){ - modClue = newClue; + public ArrayList getCol() { //NORTH CLUE + return col; } @Override @@ -103,10 +70,6 @@ public SkyscrapersCell getCell(int x, int y) { return (SkyscrapersCell) super.getCell(x, y); } - public int getSize() { - return this.getWidth(); - } - @Override public PuzzleElement getPuzzleElement(PuzzleElement element) { switch (element.getIndex()) { @@ -156,13 +119,6 @@ public void notifyDeletion(PuzzleElement puzzleElement) { } } - /** - * Gets the cells of a certain type directly adjacent to a given cell - * - * @param cell at the center, - * type of cell to collect - * @return list of cells of the given type - */ public List getAdjacent(SkyscrapersCell cell, SkyscrapersType type) { List adj = new ArrayList<>(); Point loc = cell.getLocation(); @@ -170,28 +126,21 @@ public List getAdjacent(SkyscrapersCell cell, SkyscrapersType t SkyscrapersCell right = getCell(loc.x + 1, loc.y); SkyscrapersCell down = getCell(loc.x, loc.y + 1); SkyscrapersCell left = getCell(loc.x - 1, loc.y); - if (up != null && (up.getType() == type || type == SkyscrapersType.ANY)) { + if (up != null && up.getType() == type) { adj.add(up); } - if (right != null && (right.getType() == type || type == SkyscrapersType.ANY)) { + if (right != null && right.getType() == type) { adj.add(right); } - if (down != null && (down.getType() == type || type == SkyscrapersType.ANY)) { + if (down != null && down.getType() == type) { adj.add(down); } - if (left != null && (left.getType() == type || type == SkyscrapersType.ANY)) { + if (left != null && left.getType() == type) { adj.add(left); } return adj; } - /** - * Gets the cells of a certain type directly diagonal to a given cell - * - * @param cell at the center, - * type of cell to collect - * @return list of cells of the given type - */ public List getDiagonals(SkyscrapersCell cell, SkyscrapersType type) { List dia = new ArrayList<>(); Point loc = cell.getLocation(); @@ -199,62 +148,40 @@ public List getDiagonals(SkyscrapersCell cell, SkyscrapersType SkyscrapersCell downRight = getCell(loc.x + 1, loc.y + 1); SkyscrapersCell downLeft = getCell(loc.x - 1, loc.y + 1); SkyscrapersCell upLeft = getCell(loc.x - 1, loc.y - 1); - if (upRight != null && (upRight.getType() == type || type == SkyscrapersType.ANY)) { + if (upRight != null && upRight.getType() == type) { dia.add(upRight); } - if (downLeft != null && (downLeft.getType() == type || type == SkyscrapersType.ANY)) { + if (downLeft != null && downLeft.getType() == type) { dia.add(downLeft); } - if (downRight != null && (downRight.getType() == type || type == SkyscrapersType.ANY)) { + if (downRight != null && downRight.getType() == type) { dia.add(downRight); } - if (upLeft != null && (upLeft.getType() == type || type == SkyscrapersType.ANY)) { + if (upLeft != null && upLeft.getType() == type) { dia.add(upLeft); } return dia; } - /** - * Gets the cells of a certain type in a given row/column - * - * @param index: y pos of row or x pos of col, - * type of cell to collect, - * boolean true if row, false if col - * @return list of cells of the given type, ordered west to east or north to south - */ public List getRowCol(int index, SkyscrapersType type, boolean isRow) { List list = new ArrayList<>(); - for (int i = 0; i < dimension.height; i++) { - SkyscrapersCell cell; - if (isRow) { - cell = getCell(i, index); - } - else { - cell = getCell(index, i); - } - - if (cell.getType() == type || type == SkyscrapersType.ANY) { - list.add(cell); + if (isRow) { + for (int i = 0; i < dimension.height; i++) { + SkyscrapersCell cell = getCell(i, index); + if (cell.getType() == type) { + list.add(cell); + } } } - return list; - } - - /** - * Prints a semblance of the board to console (helps in debugging) - */ - public void printBoard(){ - for(int i =0; i { public SkyscrapersClue(int value, int clueIndex, SkyscrapersType type) { super(value); this.index = -2; - this.clueIndex = clueIndex;//index in list + this.clueIndex = clueIndex; this.type = type; this.setModifiable(false); } @@ -38,6 +38,10 @@ public int getClueIndex() { return clueIndex; } + public void setClueIndex(int clueIndex) { + this.clueIndex = clueIndex; + } + public SkyscrapersType getType() { return type; } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java index d070fdf4d..549789c01 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java @@ -23,15 +23,9 @@ public SkyscrapersClue getPuzzleElement() { return (SkyscrapersClue) super.getPuzzleElement(); } - @Override + @Override public void draw(Graphics2D graphics2D) { drawElement(graphics2D); - if (this.isShowCasePicker() && this.isCaseRulePickable()) { - drawCase(graphics2D); - if(this.isHover()){ - drawHover(graphics2D); - } - } } @Override 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 0558213ab..fa771beca 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java @@ -85,7 +85,17 @@ public void mouseReleased(MouseEvent e) { treePanel.updateError(edit.getError()); } } - } + } /*else if (lastCellPressed != null) { + if (dragStart instanceof SkyscrapersElementView) { + ICommand editLine = new EditLineCommand(selection, (SkyscrapersElementView) dragStart, lastCellPressed); + if (editLine.canExecute()) { + editLine.execute(); + getInstance().getHistory().pushChange(editLine); + } else { + treePanel.updateError(editLine.getError()); + } + } + }*/ } } dragStart = null; 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 533437d67..8df3a18a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java @@ -16,7 +16,30 @@ public SkyscrapersElementView(SkyscrapersCell cell) { @Override public void drawElement(Graphics2D graphics2D) { - graphics2D.setStroke(new BasicStroke(1)); + /*SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; + SkyscrapersType type = cell.getType(); + graphics2D.setStroke(new BasicStroke(0)); + if (type == SkyscrapersType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fill(new Rectangle2D.Double(location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); + graphics2D.setColor(Color.BLACK); + graphics2D.draw(new Rectangle2D.Double(location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); + } else if (type == SkyscrapersType.TREE) { + graphics2D.drawImage(SkyscrapersView.TREE, location.x, location.y, size.width, size.height, null, null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } else if (type == SkyscrapersType.GRASS) { + graphics2D.drawImage(SkyscrapersView.GRASS, location.x, location.y, size.width, size.height, null, null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } else if (type == SkyscrapersType.TENT) { + graphics2D.drawImage(SkyscrapersView.TENT, location.x, location.y, size.width, size.height, null, null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + }*/ + + graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(BACKGROUND_COLOR); graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.setColor(BORDER_COLOR); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java index dac09bd16..96a41378f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java @@ -6,8 +6,8 @@ public class SkyscrapersExporter extends PuzzleExporter { - public SkyscrapersExporter(Skyscrapers skyscrapers) { - super(skyscrapers); + public SkyscrapersExporter(Skyscrapers treeTent) { + super(treeTent); } @Override @@ -30,7 +30,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element axisEast = newDocument.createElement("axis"); axisEast.setAttribute("side", "east"); - for (SkyscrapersClue clue : board.getEastClues()) { + for (SkyscrapersClue clue : board.getRowClues()) { org.w3c.dom.Element clueElement = newDocument.createElement("clue"); clueElement.setAttribute("value", String.valueOf(clue.getData())); clueElement.setAttribute("index", SkyscrapersClue.colNumToString(clue.getIndex())); @@ -40,7 +40,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element axisSouth = newDocument.createElement("axis"); axisSouth.setAttribute("side", "south"); - for (SkyscrapersClue clue : board.getSouthClues()) { + for (SkyscrapersClue clue : board.getRowClues()) { 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/skyscrapers/SkyscrapersImporter.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java index c4c909172..68979881a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java @@ -9,8 +9,8 @@ import java.awt.*; public class SkyscrapersImporter extends PuzzleImporter { - public SkyscrapersImporter(Skyscrapers skyscrapers) { - super(skyscrapers); + public SkyscrapersImporter(Skyscrapers treeTent) { + super(treeTent); } /** @@ -35,74 +35,78 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("Skyscrapers Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException("TreeTent Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("Skyscrapers Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException("TreeTent Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); - - - SkyscrapersBoard skyscrapersBoard = null; - + SkyscrapersBoard treeTentBoard = null; if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); - skyscrapersBoard = new SkyscrapersBoard(size); + treeTentBoard = new SkyscrapersBoard(size); } - - if (skyscrapersBoard == null) { - throw new InvalidFileFormatException("Skyscraper Importer: invalid board dimensions"); + else { + if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + int width = Integer.valueOf(boardElement.getAttribute("width")); + int height = Integer.valueOf(boardElement.getAttribute("height")); + treeTentBoard = new SkyscrapersBoard(width, height); + } } - int size = skyscrapersBoard.getSize(); + if (treeTentBoard == null) { + throw new InvalidFileFormatException("TreeTent Importer: invalid board dimensions"); + } + int width = treeTentBoard.getWidth(); + int height = treeTentBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - SkyscrapersCell cell = (SkyscrapersCell) puzzle.getFactory().importCell(elementDataList.item(i), skyscrapersBoard); + SkyscrapersCell cell = (SkyscrapersCell) puzzle.getFactory().importCell(elementDataList.item(i), treeTentBoard); Point loc = cell.getLocation(); if (cell.getData() != 0) { cell.setModifiable(false); cell.setGiven(true); } - skyscrapersBoard.setCell(loc.x, loc.y, cell); + treeTentBoard.setCell(loc.x, loc.y, cell); } - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - if (skyscrapersBoard.getCell(x, y) == null) { - SkyscrapersCell cell = new SkyscrapersCell(0, new Point(x, y), size); - cell.setIndex(y * size + x); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (treeTentBoard.getCell(x, y) == null) { + SkyscrapersCell cell = new SkyscrapersCell(0, new Point(x, y), width); + cell.setIndex(y * height + x); cell.setModifiable(true); - skyscrapersBoard.setCell(x, y, cell); + treeTentBoard.setCell(x, y, cell); } } } NodeList axes = boardElement.getElementsByTagName("axis"); if (axes.getLength() != 2) { - throw new InvalidFileFormatException("Skyscraper Importer: cannot find axes"); + throw new InvalidFileFormatException("TreeTent Importer: cannot find axes"); } Element axis1 = (Element) axes.item(0); Element axis2 = (Element) axes.item(1); if (!axis1.hasAttribute("side") || !axis1.hasAttribute("side")) { - throw new InvalidFileFormatException("Skyscraper Importer: side attribute of axis not specified"); + throw new InvalidFileFormatException("TreeTent Importer: side attribute of axis not specified"); } String side1 = axis1.getAttribute("side"); String side2 = axis2.getAttribute("side"); if (side1.equalsIgnoreCase(side2) || !(side1.equalsIgnoreCase("east") || side1.equalsIgnoreCase("south")) || !(side2.equalsIgnoreCase("east") || side2.equalsIgnoreCase("south"))) { - throw new InvalidFileFormatException("Skyscraper Importer: axes must be different and be {east | south}"); + throw new InvalidFileFormatException("TreeTent Importer: axes must be different and be {east | south}"); } NodeList eastClues = side1.equalsIgnoreCase("east") ? axis1.getElementsByTagName("clue") : axis2.getElementsByTagName("clue"); NodeList southClues = side1.equalsIgnoreCase("south") ? axis1.getElementsByTagName("clue") : axis2.getElementsByTagName("clue"); - if (eastClues.getLength() != skyscrapersBoard.getHeight() || southClues.getLength() != skyscrapersBoard.getWidth()) { - throw new InvalidFileFormatException("Skyscraper Importer: there must be same number of clues as the dimension of the board"); + if (eastClues.getLength() != treeTentBoard.getHeight() || southClues.getLength() != treeTentBoard.getWidth()) { + throw new InvalidFileFormatException("TreeTent Importer: there must be same number of clues as the dimension of the board"); } for (int i = 0; i < eastClues.getLength(); i++) { @@ -110,9 +114,15 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int value = Integer.valueOf(clue.getAttribute("value")); //int index = SkyscrapersClue.colStringToColNum(clue.getAttribute("index")); int index = Integer.valueOf(clue.getAttribute("index")); + /*if (index - 1 < 0 || index - 1 > treeTentBoard.getHeight()) { + throw new InvalidFileFormatException("TreeTent Importer: clue index out of bounds"); + } - skyscrapersBoard.getWestClues().set(/*index - 1*/i, new SkyscrapersClue(index, i, SkyscrapersType.CLUE_WEST)); - skyscrapersBoard.getEastClues().set(/*index - 1*/i, new SkyscrapersClue(value, i, SkyscrapersType.CLUE_EAST)); + if (treeTentBoard.getRowClues().get(index - 1) != null) { + throw new InvalidFileFormatException("TreeTent Importer: duplicate clue index"); + }*/ + treeTentBoard.getRow().set(/*index - 1*/i, new SkyscrapersClue(index, index, SkyscrapersType.CLUE_WEST)); + treeTentBoard.getRowClues().set(/*index - 1*/i, new SkyscrapersClue(value, index, SkyscrapersType.CLUE_EAST)); } for (int i = 0; i < southClues.getLength(); i++) { @@ -120,35 +130,29 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int value = Integer.valueOf(clue.getAttribute("value")); int index = Integer.valueOf(clue.getAttribute("index")); + /*if (index - 1 < 0 || index - 1 > treeTentBoard.getWidth()) { + throw new InvalidFileFormatException("TreeTent Importer: clue index out of bounds"); + } - skyscrapersBoard.getNorthClues().set(/*index - 1*/i, new SkyscrapersClue(index, i, SkyscrapersType.CLUE_NORTH)); - skyscrapersBoard.getSouthClues().set(/*index - 1*/i, new SkyscrapersClue(value, i, SkyscrapersType.CLUE_SOUTH)); + if (treeTentBoard.getColClues().get(index - 1) != null) { + throw new InvalidFileFormatException("TreeTent Importer: duplicate clue index"); + }*/ + treeTentBoard.getCol().set(/*index - 1*/i, new SkyscrapersClue(index, index, SkyscrapersType.CLUE_NORTH)); + treeTentBoard.getColClues().set(/*index - 1*/i, new SkyscrapersClue(value, index, SkyscrapersType.CLUE_SOUTH)); } if (boardElement.getElementsByTagName("lines").getLength() == 1) { Element linesElement = (Element) boardElement.getElementsByTagName("lines").item(0); NodeList linesList = linesElement.getElementsByTagName("line"); for (int i = 0; i < linesList.getLength(); i++) { - skyscrapersBoard.getLines().add((SkyscrapersLine) puzzle.getFactory().importCell(linesList.item(i), skyscrapersBoard)); - } - } - - //Initialize present flags - NodeList flagList = boardElement.getElementsByTagName("flags"); - if(flagList.getLength()==1){ - Element flags = (Element) flagList.item(0); - if(flags.hasAttribute("dupe")){ - skyscrapersBoard.setDupeFlag(Boolean.parseBoolean(flags.getAttribute("dupe"))); - } - if(flags.hasAttribute("view")){ - skyscrapersBoard.setViewFlag(Boolean.parseBoolean(flags.getAttribute("view"))); + treeTentBoard.getLines().add((SkyscrapersLine) puzzle.getFactory().importCell(linesList.item(i), treeTentBoard)); } } - puzzle.setCurrentBoard(skyscrapersBoard); + puzzle.setCurrentBoard(treeTentBoard); } catch (NumberFormatException e) { - throw new InvalidFileFormatException("Skyscraper Importer: unknown value where integer expected"); + throw new InvalidFileFormatException("TreeTent Importer: unknown value where integer expected"); } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java index 02daa7a11..2f0f30afd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java @@ -24,6 +24,7 @@ public void draw(Graphics2D graphics2D) { int x2 = (p2.x + 1) * size.width + size.width / 2; int y2 = (p2.y + 1) * size.height + size.height / 2; + //graphics2D.setColor(LINE_COLOR); graphics2D.setColor(line.isModified() ? Color.GREEN : Color.WHITE); graphics2D.setStroke(LINE_STROKE); graphics2D.drawLine(x1, y1, x2, y2); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java index 1fdd668c1..3340af8bf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java @@ -1,7 +1,7 @@ package edu.rpi.legup.puzzle.skyscrapers; public enum SkyscrapersType { - UNKNOWN(0), Number(1), ANY(2), CLUE_NORTH(-1), CLUE_EAST(-2), CLUE_SOUTH(-3), CLUE_WEST(-4); + UNKNOWN(0), Number(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/skyscrapers/SkyscrapersView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java index b12867eda..e165686bb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java @@ -1,9 +1,9 @@ package edu.rpi.legup.puzzle.skyscrapers; import edu.rpi.legup.controller.BoardController; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.GridBoardView; import org.apache.logging.log4j.LogManager; @@ -16,8 +16,21 @@ public class SkyscrapersView extends GridBoardView { private final static Logger LOGGER = LogManager.getLogger(SkyscrapersView.class.getName()); + static Image TREE, GRASS, TENT; + + static { + try { + TREE = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/treetent/tree.png")); + GRASS = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/treetent/grass.png")); + TENT = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/treetent/tent.png")); + } + catch (IOException e) { + LOGGER.error("Failed to open TreeTent images"); + } + } private ArrayList lineViews; + private ArrayList northClues; private ArrayList eastClues; private ArrayList southClues; @@ -50,11 +63,12 @@ public SkyscrapersView(SkyscrapersBoard board) { } for (int i = 0; i < gridSize.height; i++) { - SkyscrapersClueView row = new SkyscrapersClueView(board.getWestClues().get(i)); + //SkyscrapersClueView row = new SkyscrapersClueView(new SkyscrapersClue(i, i, SkyscrapersType.CLUE_WEST)); + SkyscrapersClueView row = new SkyscrapersClueView(board.getRow().get(i)); row.setLocation(new Point(0, (i + 1) * elementSize.height)); row.setSize(elementSize); - SkyscrapersClueView clue = new SkyscrapersClueView(board.getEastClues().get(i)); + SkyscrapersClueView clue = new SkyscrapersClueView(board.getRowClues().get(i)); clue.setLocation(new Point((gridSize.height + 1) * elementSize.height, (i + 1) * elementSize.height)); clue.setSize(elementSize); @@ -63,11 +77,12 @@ public SkyscrapersView(SkyscrapersBoard board) { } for (int i = 0; i < gridSize.width; i++) { - SkyscrapersClueView col = new SkyscrapersClueView(board.getNorthClues().get(i)); + //SkyscrapersClueView col = new SkyscrapersClueView(new SkyscrapersClue(i, i, SkyscrapersType.CLUE_NORTH)); + SkyscrapersClueView col = new SkyscrapersClueView(board.getCol().get(i)); col.setLocation(new Point((i + 1) * elementSize.width, 0)); col.setSize(elementSize); - SkyscrapersClueView clue = new SkyscrapersClueView(board.getSouthClues().get(i)); + SkyscrapersClueView clue = new SkyscrapersClueView(board.getColClues().get(i)); clue.setLocation(new Point((i + 1) * elementSize.width, (gridSize.width + 1) * elementSize.width)); clue.setSize(elementSize); @@ -114,10 +129,22 @@ public ElementView getElement(Point point) { return null; } + public ArrayList getLineViews() { + return lineViews; + } + public ArrayList getNorthClues() { return northClues; } + public ArrayList getEastClues() { + return eastClues; + } + + public ArrayList getSouthClues() { + return southClues; + } + public ArrayList getWestClues() { return westClues; } @@ -131,57 +158,26 @@ protected Dimension getProperSize() { } /** - * Sets the board associated with this view + * Called when the tree element has changed. * - * @param board board + * @param treeElement tree element */ @Override - public void setBoard(Board board) { - if (this.board != board) { - this.board = board; - - if (board instanceof CaseBoard) { - setCasePickable(); - } - else { - for (ElementView elementView : elementViews) { - elementView.setPuzzleElement(board.getPuzzleElement(elementView.getPuzzleElement())); - elementView.setShowCasePicker(false); - } - for (SkyscrapersClueView clueView : northClues) { - clueView.setPuzzleElement(board.getPuzzleElement(clueView.getPuzzleElement())); - clueView.setShowCasePicker(false); - } - for (SkyscrapersClueView clueView : westClues) { - clueView.setPuzzleElement(board.getPuzzleElement(clueView.getPuzzleElement())); - clueView.setShowCasePicker(false); - } - } - } - } - - @Override - protected void setCasePickable() { - CaseBoard caseBoard = (CaseBoard) board; - Board baseBoard = caseBoard.getBaseBoard(); - - for (ElementView elementView : elementViews) { - PuzzleElement puzzleElement = baseBoard.getPuzzleElement(elementView.getPuzzleElement()); - elementView.setPuzzleElement(puzzleElement); - elementView.setShowCasePicker(true); - elementView.setCaseRulePickable(caseBoard.isPickable(puzzleElement, null)); + public void onTreeElementChanged(TreeElement treeElement) { + super.onTreeElementChanged(treeElement); + SkyscrapersBoard treeTentBoard; + if (board instanceof CaseBoard) { + treeTentBoard = (SkyscrapersBoard) ((CaseBoard) board).getBaseBoard(); } - for (SkyscrapersClueView clueView : northClues) { - PuzzleElement puzzleElement = baseBoard.getPuzzleElement(clueView.getPuzzleElement()); - clueView.setPuzzleElement(puzzleElement); - clueView.setShowCasePicker(true); - clueView.setCaseRulePickable(caseBoard.isPickable(puzzleElement, null)); + else { + treeTentBoard = (SkyscrapersBoard) board; } - for (SkyscrapersClueView clueView : westClues) { - PuzzleElement puzzleElement = baseBoard.getPuzzleElement(clueView.getPuzzleElement()); - clueView.setPuzzleElement(puzzleElement); - clueView.setShowCasePicker(true); - clueView.setCaseRulePickable(caseBoard.isPickable(puzzleElement, null)); + + lineViews.clear(); + for (SkyscrapersLine line : treeTentBoard.getLines()) { + SkyscrapersLineView lineView = new SkyscrapersLineView(line); + lineView.setSize(elementSize); + lineViews.add(lineView); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/TODO.md b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/TODO.md new file mode 100644 index 000000000..76c86b32f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/TODO.md @@ -0,0 +1,11 @@ +## TODO + +- Implement board class (`SkyscrapersBoard.java`) +- Implement clues class (`SkyscrapersClue.java`) +- Implement cell generation (`SkyscrapersCellFactory.java`) +- Implement puzzle exporter (`SkyscrapersExporter.java`) +- Add way to interact with puzzle (`SkyscrapersCellController.java`) +- Add vision (`SkyscrapersVision.java`, `SkyscrapersVisionView.java`) +- Puzzle GUI (`SkyscrapersView.java`, `SkyscrapersElementView.java`, `SkyscrapersClueView.java`) +- Determine if puzzle is solved +- Add rules (see Sudoku and the powerpoint) diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java deleted file mode 100644 index 01a1557b0..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java +++ /dev/null @@ -1,135 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -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.skyscrapers.*; -import org.apache.commons.lang3.ObjectUtils; - -import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -public class CellForNumberCaseRule extends CaseRule { - public CellForNumberCaseRule() { - super("SKYS-CASE-0002", "Cell For Number", - "A number (1-n) must appear in any given row/column", - "edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png"); - } - - private Integer selectedNumber; - - @Override - public CaseBoard getCaseBoard(Board board) { - SkyscrapersBoard currentBoard = (SkyscrapersBoard) board.copy(); - currentBoard.setModifiable(false); - CaseBoard caseBoard = new CaseBoard(currentBoard, this); - for (SkyscrapersClue data : currentBoard.getWestClues()) { - //System.out.println(data.getType()); - caseBoard.addPickableElement(data); - } - for (SkyscrapersClue data : currentBoard.getNorthClues()) { - //System.out.println(data.getType()); - caseBoard.addPickableElement(data); - } - - //selects integer before checking Command.canExecute for use in Command.getErrorString - int size = ((SkyscrapersBoard)board).getWidth(); - Object[] possibleValues = new Object[size]; - for(int i=0; i getCasesFor(Board board, PuzzleElement puzzleElement, Integer number){ - ArrayList cases = new ArrayList<>(); - - SkyscrapersClue clue = (SkyscrapersClue) puzzleElement; - SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; - - List openCells = skyscrapersboard.getRowCol(clue.getClueIndex(),SkyscrapersType.UNKNOWN,clue.getType()==SkyscrapersType.CLUE_WEST); - for(SkyscrapersCell cell : openCells){ - SkyscrapersBoard newCase = skyscrapersboard.copy(); - PuzzleElement newCell = newCase.getPuzzleElement(cell); - newCell.setData(number); - newCase.addModifiedData(newCell); - newCase.setModClue((SkyscrapersClue)newCase.getPuzzleElement(clue)); - - //if flags - boolean passed = true; - if(skyscrapersboard.getDupeFlag()){ - DuplicateNumberContradictionRule DupeRule = new DuplicateNumberContradictionRule(); - passed = passed && DupeRule.checkContradictionAt(newCase,newCell)!=null; - } - if(skyscrapersboard.getViewFlag()){ - PreemptiveVisibilityContradictionRule ViewRule = new PreemptiveVisibilityContradictionRule(); - passed = passed && ViewRule.checkContradictionAt(newCase,newCell)!=null; - } - if(passed){ - cases.add(newCase); - } - - - } - return cases; - } - - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - return getCasesFor(board,puzzleElement,selectedNumber); - } - - @Override - public String checkRuleRaw(TreeTransition transition) { - List childTransitions = transition.getParents().get(0).getChildren(); - SkyscrapersBoard oldBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - if (childTransitions.size() == 0) { - return "This case rule must have at least one child."; - } - - //find changed row/col - SkyscrapersClue modClue = ((SkyscrapersBoard)childTransitions.get(0).getBoard()).getmodClue(); - - //System.out.println(modClue.getType()); - //System.out.println(modClue.getClueIndex()); - if(childTransitions.size() != getCasesFor(oldBoard,modClue,(Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()){ - //System.out.println("Wrong number of cases."); - return "Wrong number of cases."; - } - - for(TreeTransition newTree : childTransitions){ - SkyscrapersBoard newBoard = (SkyscrapersBoard) newTree.getBoard(); - if(newBoard.getModifiedData().size()!=1){ - //System.out.println("Only one cell should be modified."); - return "Only one cell should be modified."; - } - SkyscrapersCell newCell = (SkyscrapersCell) newBoard.getModifiedData().iterator().next(); - if(newCell.getType() != SkyscrapersType.Number){ - //System.out.println("Changed value should be a number."); - return "Changed value should be a number."; - } - } - return null; - } - - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return checkRuleRaw(transition); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java index c0508bf67..a7df882ff 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java @@ -16,7 +16,7 @@ public class DuplicateNumberContradictionRule extends ContradictionRule { public DuplicateNumberContradictionRule() { super("SKYS-CONT-0001", "Duplicate Number", "Skyscrapers of same height cannot be placed in the same row or column.", - "edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png"); + "edu/rpi/legup/images/skyscrapers/DuplicateNumber.png"); } /** @@ -29,8 +29,7 @@ public DuplicateNumberContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - //TODO:? Refactor to count each row/col once rather than per cell (override checkContradiction) - SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; + SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; Point loc = cell.getLocation(); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java index ef8f72711..9a9c0bf8e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java @@ -8,9 +8,7 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; import java.awt.*; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; public class ExceedingVisibilityContradictionRule extends ContradictionRule { @@ -18,7 +16,7 @@ public class ExceedingVisibilityContradictionRule extends ContradictionRule { public ExceedingVisibilityContradictionRule() { super("SKYS-CONT-0002", "Exceeding Visibility", "More skyscrapers are visible than there should be.", - "edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png"); + "edu/rpi/legup/images/skyscrapers/ExceedingVisibility.png"); } /** @@ -34,106 +32,92 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; Point loc = cell.getLocation(); - - //get borders - int west = skyscrapersboard.getWestClues().get(loc.y).getData(); - int east = skyscrapersboard.getEastClues().get(loc.y).getData(); - int north = skyscrapersboard.getNorthClues().get(loc.x).getData(); - int south = skyscrapersboard.getSouthClues().get(loc.x).getData(); - //check row - int max = 0; - int count = 0; - List row = skyscrapersboard.getRowCol(loc.y,SkyscrapersType.Number,true); - if(row.size()==skyscrapersboard.getWidth()){ - //from west border - for(SkyscrapersCell c : row){ - if (c.getData() > max) { - System.out.print(c.getData()); - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count > west) { - return null; - } + Set candidates = new HashSet(); - max = 0; - count = 0; - //from east border - Collections.reverse(row); - for(SkyscrapersCell c : row){ - if (c.getData() > max) { - System.out.print(c.getData()); - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count > east) { - return null; - } - } - - //check column - List col = skyscrapersboard.getRowCol(loc.x,SkyscrapersType.Number,false); - if(col.size()==skyscrapersboard.getHeight()){ - //from north border - max = 0; - count = 0; - for(SkyscrapersCell c : col){ - System.out.println(c.getData()); - if (c.getData() > max) { + //check row + int west = skyscrapersboard.getRow().get(loc.y).getData(); + int east = skyscrapersboard.getRowClues().get(loc.y).getData(); + int north = skyscrapersboard.getCol().get(loc.x).getData(); + int south = skyscrapersboard.getColClues().get(loc.x).getData(); + int max = 0; + int count = 0; + boolean complete = true; + for (int i = 0; i < skyscrapersboard.getWidth(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count++; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count > west && complete == true) { + return null; + } - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count > north) { - return null; - } + max = 0; + count = 0; + complete = true; + for (int i = skyscrapersboard.getWidth() - 1; i >= 0; i--) { + SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count = count + 1; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count > east && complete == true) { + return null; + } - //from south border - max = 0; - count = 0; - Collections.reverse(col); - for(SkyscrapersCell c : col){ - System.out.println(c.getData()); - if (c.getData() > max) { + // check column + max = 0; + count = 0; + complete = true; + for (int i = 0; i < skyscrapersboard.getHeight(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count = count + 1; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count > north && complete == true) { + return null; + } + + max = 0; + count = 0; + complete = true; + for (int i = skyscrapersboard.getHeight() - 1; i >= 0; i--) { + SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count = count + 1; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count > south && complete == true) { + return null; + } - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count > south) { - return null; - } - } - //System.out.print("Does not contain a contradiction at this index"); return super.getNoContradictionMessage(); } - - /** - * Checks whether the Skyscraper cell has a contradiction using this rule - * - * @param board board to check contradiction - * @return null if the Skyscraper cell contains a contradiction, otherwise error message - */ - @Override - public String checkContradiction(Board board) { - SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) - String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i,i)); - if (checkStr == null) { - return checkStr; - } - } - return "No instance of the contradiction " + this.ruleName + " here"; - } - } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/FixedMaxBasicRule.java similarity index 63% rename from src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberBasicRule.java rename to src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/FixedMaxBasicRule.java index 248998e84..85b6fe62d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/FixedMaxBasicRule.java @@ -9,14 +9,17 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; -import java.util.ArrayList; +import java.awt.Point; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; -public class LastSingularNumberBasicRule extends BasicRule { +public class FixedMaxBasicRule extends BasicRule { - public LastSingularNumberBasicRule() { - super("SKYS-BASC-0003", "Last Non-Duplicate Number", - "There is only one number for this cell that does not create a duplicate contradiction", - "edu/rpi/legup/images/skyscrapers/rules/LastNumber.png"); + public FixedMaxBasicRule() { + super("SKYS-BASC-0001", "Fixed Max", + "If the sum of two opposing edges is n+1, the maximum number appears at a position k spaces away from the edge, where k is the number at that edge.", + "edu/rpi/legup/images/skyscrapers/FixedMax.png"); } /** @@ -34,28 +37,38 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (initCell.getType() != SkyscrapersType.UNKNOWN || finalCell.getType() != SkyscrapersType.Number) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must transition from unknown to number"; + if (!(initCell.getType() == SkyscrapersType.UNKNOWN && finalCell.getType() == SkyscrapersType.Number)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; } - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(true); - initialBoard.setViewFlag(false); - NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); - ArrayList candidates = caseRule.getCases(initialBoard,puzzleElement); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); + SkyscrapersBoard emptyCase = initialBoard.copy(); + emptyCase.getPuzzleElement(finalCell).setData(0); + Point loc = finalCell.getLocation(); + int north = initialBoard.getCol().get(loc.x).getData(); + int south = initialBoard.getColClues().get(loc.x).getData(); + int west = initialBoard.getRow().get(loc.y).getData(); + int east = initialBoard.getRowClues().get(loc.y).getData(); + int max = initialBoard.getHeight(); + System.out.println(north); + System.out.println(south); + if (north + south != max + 1 && west + east != max + 1) { + System.out.println("111"); + return super.getInvalidUseOfRuleMessage() + ": Opposing clues must add up to max"; + } - //check if given value is the only remaining value - if(candidates.size() == 1){ - if(candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()){ - return null; - } - return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + if (finalCell.getData() != initialBoard.getWidth()) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be the max"; + } + + if (north + south == max + 1 && loc.y + 1 == north) { + return null; } - return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; + if (west + east == max + 1 && loc.x + 1 == west) { + return null; + } + + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced."; + } private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java index fb3764a2b..ef0968710 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java @@ -8,9 +8,7 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; import java.awt.*; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; public class InsufficientVisibilityContradictionRule extends ContradictionRule { @@ -18,7 +16,7 @@ public class InsufficientVisibilityContradictionRule extends ContradictionRule { public InsufficientVisibilityContradictionRule() { super("SKYS-CONT-0003", "Insufficient Visibility", "Less skyscrapers are visible than there should be.", - "edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png"); + "edu/rpi/legup/images/skyscrapers/InsufficientVisibility.png"); } /** @@ -35,104 +33,91 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; Point loc = cell.getLocation(); - //get borders - int west = skyscrapersboard.getWestClues().get(loc.y).getData(); - int east = skyscrapersboard.getEastClues().get(loc.y).getData(); - int north = skyscrapersboard.getNorthClues().get(loc.x).getData(); - int south = skyscrapersboard.getSouthClues().get(loc.x).getData(); + Set candidates = new HashSet(); - //check row - int max = 0; - int count = 0; - java.util.List row = skyscrapersboard.getRowCol(loc.y,SkyscrapersType.Number,true); - if(row.size()==skyscrapersboard.getWidth()){ - //from west border - for(SkyscrapersCell c : row){ - if (c.getData() > max) { - System.out.print(c.getData()); - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count < west) { - return null; - } + //check row + int west = skyscrapersboard.getRow().get(loc.y).getData(); + int east = skyscrapersboard.getRowClues().get(loc.y).getData(); + int north = skyscrapersboard.getCol().get(loc.x).getData(); + int south = skyscrapersboard.getColClues().get(loc.x).getData(); + int max = 0; + int count = 0; + boolean complete = true; + for (int i = 0; i < skyscrapersboard.getWidth(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count++; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count < west && complete == true) { + return null; + } - max = 0; - count = 0; - //from east border - Collections.reverse(row); - for(SkyscrapersCell c : row){ - if (c.getData() > max) { - System.out.print(c.getData()); - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count < east) { - return null; - } - } + max = 0; + count = 0; + complete = true; + for (int i = skyscrapersboard.getWidth() - 1; i >= 0; i--) { + SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count = count + 1; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count < east && complete == true) { + return null; + } - //check column - List col = skyscrapersboard.getRowCol(loc.x,SkyscrapersType.Number,false); - if(col.size()==skyscrapersboard.getHeight()){ - //from north border - max = 0; - count = 0; - for(SkyscrapersCell c : col){ - System.out.println(c.getData()); - if (c.getData() > max) { + // check column + max = 0; + count = 0; + complete = true; + for (int i = 0; i < skyscrapersboard.getHeight(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count = count + 1; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count < north && complete == true) { + return null; + } - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count < north) { - return null; - } + max = 0; + count = 0; + complete = true; + for (int i = skyscrapersboard.getHeight() - 1; i >= 0; i--) { + SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); + if (c.getType() == SkyscrapersType.Number && c.getData() > max) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + max = c.getData(); + count = count + 1; + } + if (c.getType() == SkyscrapersType.UNKNOWN) { + complete = false; + } + } + if (count < south && complete == true) { + return null; + } - //from south border - max = 0; - count = 0; - Collections.reverse(col); - for(SkyscrapersCell c : col){ - System.out.println(c.getData()); - if (c.getData() > max) { - - //System.out.println(cell.getData()); - max = c.getData(); - count++; - } - } - if (count < south) { - return null; - } - } - //System.out.print("Does not contain a contradiction at this index"); return super.getNoContradictionMessage(); } - - /** - * Checks whether the Skyscraper cell has a contradiction using this rule - * - * @param board board to check contradiction - * @return null if the Skyscraper cell contains a contradiction, otherwise error message - */ - @Override - public String checkContradiction(Board board) { - SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) - String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i,i)); - if (checkStr == null) { - return checkStr; - } - } - return "No instance of the contradiction " + this.ruleName + " here"; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastCellBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastCellBasicRule.java new file mode 100644 index 000000000..524c36319 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastCellBasicRule.java @@ -0,0 +1,132 @@ +package edu.rpi.legup.puzzle.skyscrapers.rules; + +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.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; + +import java.awt.Point; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public class LastCellBasicRule extends BasicRule { + + public LastCellBasicRule() { + super("SKYS-BASC-0002", "Last Cell", + "A certain number must go in a certain cell, because that cell is the last place that number can appear in that row/column.", + "edu/rpi/legup/images/skyscrapers/LastCell.png"); + } + + /** + * Checks whether the child node logically follows from the parent node + * at the specific puzzleElement index using this rule + * + * @param transition transition to check + * @param puzzleElement index of the puzzleElement + * @return null if the child node logically follow from the parent node at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); + SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); + SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == SkyscrapersType.UNKNOWN && finalCell.getType() == SkyscrapersType.Number)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; + } + + SkyscrapersBoard emptyCase = initialBoard.copy(); + emptyCase.getPuzzleElement(finalCell).setData(0); + Point loc = finalCell.getLocation(); + + Set candidates = new HashSet(); + for (int i = 1; i <= initialBoard.getWidth(); i++) { + candidates.add(i); + } + + //check row + for (int i = 0; i < initialBoard.getWidth(); i++) { + SkyscrapersCell c = initialBoard.getCell(i, loc.y); + if (i != loc.x && c.getType() == SkyscrapersType.Number) { + candidates.remove(c.getData()); + //System.out.print(c.getData()); + //System.out.println(finalCell.getData()); + } + } + if (candidates.size() == 1) { + Iterator it = candidates.iterator(); + if (it.next() == finalCell.getData()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + } + + candidates.clear(); + for (int i = 1; i <= initialBoard.getWidth(); i++) { + candidates.add(i); + } + + // check column + for (int i = 0; i < initialBoard.getHeight(); i++) { + SkyscrapersCell c = initialBoard.getCell(loc.x, i); + if (i != loc.y && c.getType() == SkyscrapersType.Number) { + candidates.remove(c.getData()); + //System.out.print(c.getData()); + //System.out.println(finalCell.getData()); + } + } + if (candidates.size() == 1) { + Iterator it = candidates.iterator(); + if (it.next() == finalCell.getData()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + } + + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced."; + } + + private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { + SkyscrapersBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(0); + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (duplicate.checkContradictionAt(emptyCase, cell) == null) { + System.out.println("no contradiction ln"); + return true; + } + return false; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); + SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); + System.out.println(lightUpBoard.getPuzzleElements().size()); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + System.out.println("123"); + SkyscrapersCell cell = (SkyscrapersCell) element; + if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { + //cell.setData(SkyscrapersType.BULB.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } + else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastNumberBasicRule.java similarity index 58% rename from src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellBasicRule.java rename to src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastNumberBasicRule.java index 727559083..a4f36cd04 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastNumberBasicRule.java @@ -9,14 +9,17 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; -import java.util.ArrayList; +import java.awt.Point; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; -public class LastSingularCellBasicRule extends BasicRule { +public class LastNumberBasicRule extends BasicRule { - public LastSingularCellBasicRule() { - super("SKYS-BASC-0002", "Last Non-Duplicate Cell", - "There is only one cell on this row/col for this number that does not create a duplicate contradiction", - "edu/rpi/legup/images/skyscrapers/rules/LastCell.png"); + public LastNumberBasicRule() { + super("SKYS-BASC-0003", "Last Number", + "A certain cell must contain a certain number since that number is the only one that can possibly appear in that cell.", + "edu/rpi/legup/images/skyscrapers/LastNumber.png"); } /** @@ -38,41 +41,45 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; } - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(true); - initialBoard.setViewFlag(false); - CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - ArrayList XCandidates = caseRule.getCasesFor(initialBoard,initialBoard.getWestClues().get(finalCell.getLocation().y),(Integer)finalCell.getData()); - ArrayList YCandidates = caseRule.getCasesFor(initialBoard,initialBoard.getNorthClues().get(finalCell.getLocation().x),(Integer)finalCell.getData()); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); + SkyscrapersBoard emptyCase = initialBoard.copy(); + emptyCase.getPuzzleElement(finalCell).setData(0); + Point loc = finalCell.getLocation(); - System.out.println(XCandidates.size()); - System.out.println(YCandidates.size()); + Set candidates = new HashSet(); + for (int i = 1; i <= initialBoard.getWidth(); i++) { + candidates.add(i); + } - //return null if either pass, both messages otherwise - String xCheck = candidateCheck(XCandidates,puzzleElement,finalCell); - String yCheck = candidateCheck(YCandidates,puzzleElement,finalCell); - if(xCheck==null || yCheck==null){ - return null; + //check row + for (int i = 0; i < initialBoard.getWidth(); i++) { + SkyscrapersCell c = initialBoard.getCell(i, loc.y); + if (i != loc.x && c.getType() == SkyscrapersType.Number) { + candidates.remove(c.getData()); + //System.out.print(c.getData()); + //System.out.println(finalCell.getData()); + } } - return super.getInvalidUseOfRuleMessage() + "\nRow" + xCheck + "\nCol" + yCheck; - } - //helper to check if candidate list is valid - private String candidateCheck(ArrayList candidates,PuzzleElement puzzleElement, SkyscrapersCell finalCell){ - if(candidates.size() == 1){ - if(((SkyscrapersCell) candidates.get(0).getPuzzleElement(puzzleElement)).getType() == SkyscrapersType.Number) { - if (candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()) { - return null; - } - return ": Wrong number in the cell."; + // check column + for (int i = 0; i < initialBoard.getHeight(); i++) { + SkyscrapersCell c = initialBoard.getCell(loc.x, i); + if (i != loc.y && c.getType() == SkyscrapersType.Number) { + candidates.remove(c.getData()); + //System.out.print(c.getData()); + //System.out.println(finalCell.getData()); } - return ": No case for this cell."; } - return ": This cell is not forced."; + + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (candidates.size() == 1 && duplicate.checkContradictionAt(emptyCase, finalCell) != null) { + Iterator it = candidates.iterator(); + if (it.next() == finalCell.getData()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + } + + return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; } private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellBasicRule.java deleted file mode 100644 index d28ed6cbd..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellBasicRule.java +++ /dev/null @@ -1,117 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.skyscrapers.SkyscrapersBoard; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - -import java.util.ArrayList; - -public class LastVisibleCellBasicRule extends BasicRule { - - public LastVisibleCellBasicRule() { - super("SKYS-BASC-0001", "Last Visible Cell", - "There is only one cell on this row/col for this number that does not create a visibility contradiction", - "edu/rpi/legup/images/skyscrapers/rules/FixedMax.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement index of the puzzleElement - * @return null if the child node logically follow from the parent node at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - //last cell for number based on preemptive visibility rules - SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); - SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); - SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (initCell.getType() != SkyscrapersType.UNKNOWN || finalCell.getType() != SkyscrapersType.Number) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must transition from unknown to number"; - } - - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(false); - initialBoard.setViewFlag(true); - CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - ArrayList XCandidates = caseRule.getCasesFor(initialBoard,initialBoard.getWestClues().get(finalCell.getLocation().y),(Integer)finalCell.getData()); - ArrayList YCandidates = caseRule.getCasesFor(initialBoard,initialBoard.getNorthClues().get(finalCell.getLocation().x),(Integer)finalCell.getData()); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); - - System.out.println(XCandidates.size()); - System.out.println(YCandidates.size()); - - //return null if either pass, both messages otherwise - String xCheck = candidateCheck(XCandidates,puzzleElement,finalCell); - String yCheck = candidateCheck(YCandidates,puzzleElement,finalCell); - if(xCheck==null || yCheck==null){ - return null; - } - return super.getInvalidUseOfRuleMessage() + "\nRow" + xCheck + "\nCol" + yCheck; - } - - //helper to check if candidate list is valid - private String candidateCheck(ArrayList candidates,PuzzleElement puzzleElement, SkyscrapersCell finalCell){ - if(candidates.size() == 1){ - if(((SkyscrapersCell) candidates.get(0).getPuzzleElement(puzzleElement)).getType() == SkyscrapersType.Number) { - if (candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()) { - return null; - } - return ": Wrong number in the cell."; - } - return ": No case for this cell."; - } - return ": This cell is not forced."; - } - - private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { - SkyscrapersBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(0); - DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); - if (duplicate.checkContradictionAt(emptyCase, cell) == null) { - System.out.println("no contradiction ln"); - return true; - } - return false; - } - - /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. - * - * @param node tree node used to create default transition board - * @return default board or null if this rule cannot be applied to this tree node - */ - @Override - public Board getDefaultBoard(TreeNode node) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); - SkyscrapersBoard modBoard = (SkyscrapersBoard) node.getBoard().copy(); - System.out.println(modBoard.getPuzzleElements().size()); - for (PuzzleElement element : modBoard.getPuzzleElements()) { - System.out.println("123"); - SkyscrapersCell cell = (SkyscrapersCell) element; - if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { - //cell.setData(SkyscrapersType.BULB.value); - modBoard.addModifiedData(cell); - } - } - System.out.println(modBoard.getModifiedData().isEmpty()); - if (modBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return modBoard; - } - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeBasicRule.java index 3bea76813..d5c58220e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeBasicRule.java @@ -19,7 +19,7 @@ public class NEdgeBasicRule extends BasicRule { public NEdgeBasicRule() { super("SKYS-BASC-0004", "N Edge", "If the maximum number appears on an edge, the row or column��s numbers appear in ascending order, starting at that edge.", - "edu/rpi/legup/images/skyscrapers/rules/NEdge.png"); + "edu/rpi/legup/images/skyscrapers/NEdge.png"); } /** @@ -46,16 +46,16 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem Point loc = finalCell.getLocation(); int max = initialBoard.getHeight(); - if (initialBoard.getWestClues().get(loc.y).getData() == max && finalCell.getData() == loc.x + 1) { + if (initialBoard.getRow().get(loc.y).getData() == max && finalCell.getData() == loc.x + 1) { return null; } - if (initialBoard.getEastClues().get(loc.y).getData() == max && finalCell.getData() == max - loc.x) { + if (initialBoard.getRowClues().get(loc.y).getData() == max && finalCell.getData() == max - loc.x) { return null; } - if (initialBoard.getNorthClues().get(loc.x).getData() == max && finalCell.getData() == loc.y + 1) { + if (initialBoard.getCol().get(loc.x).getData() == max && finalCell.getData() == loc.y + 1) { return null; } - if (initialBoard.getSouthClues().get(loc.x).getData() == max && finalCell.getData() == max - loc.y) { + if (initialBoard.getColClues().get(loc.x).getData() == max && finalCell.getData() == max - loc.y) { return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberBasicRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/OneEdgeBasicRule.java similarity index 63% rename from src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberBasicRule.java rename to src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/OneEdgeBasicRule.java index 7a4a7210a..f883d7cee 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberBasicRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/OneEdgeBasicRule.java @@ -9,14 +9,17 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; -import java.util.ArrayList; +import java.awt.Point; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; -public class LastVisibleNumberBasicRule extends BasicRule { +public class OneEdgeBasicRule extends BasicRule { - public LastVisibleNumberBasicRule() { - super("SKYS-BASC-0005", "Last Visible Number", - "There is only one number for this cell that does not create a visibility contradiction", - "edu/rpi/legup/images/skyscrapers/rules/OneEdge.png"); + public OneEdgeBasicRule() { + super("SKYS-BASC-0005", "One Edge", + "If you have a 1 on an edge, put n in the adjacent square.", + "edu/rpi/legup/images/skyscrapers/OneEdge.png"); } /** @@ -30,33 +33,48 @@ public LastVisibleNumberBasicRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - //last number for cell based upon preemptive visibility rules SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (initCell.getType() != SkyscrapersType.UNKNOWN || finalCell.getType() != SkyscrapersType.Number) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must transition from unknown to number"; + if (!(initCell.getType() == SkyscrapersType.UNKNOWN && finalCell.getType() == SkyscrapersType.Number)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; } - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(false); - initialBoard.setViewFlag(true); - NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); - ArrayList candidates = caseRule.getCases(initialBoard,puzzleElement); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); + SkyscrapersBoard emptyCase = initialBoard.copy(); + emptyCase.getPuzzleElement(finalCell).setData(0); + Point loc = finalCell.getLocation(); - //check if given value is the only remaining value - if(candidates.size() == 1){ - if(candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()){ + if (loc.x != 0 && loc.x != initialBoard.getWidth() - 1 && loc.y != 0 && loc.y != initialBoard.getHeight() - 1) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be on the edge"; + } + + if (finalCell.getData() != initialBoard.getWidth()) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be the max"; + } + + if (loc.x == 0 && initialBoard.getRow().get(loc.y).getData() == 1) { + return null; + } + else { + if (loc.x == initialBoard.getWidth() - 1 && initialBoard.getRowClues().get(loc.y).getData() == 1) { return null; } - return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + else { + if (loc.y == 0 && initialBoard.getCol().get(loc.x).getData() == 1) { + return null; + } + else { + if (loc.y == initialBoard.getHeight() - 1 && initialBoard.getColClues().get(loc.x).getData() == 1) { + return null; + } + else { + return "This cell is not forced."; + } + } + } } - return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; + } private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PossibleContentsCaseRule.java similarity index 75% rename from src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PossibleContentsCaseRule.java index abf6a10b0..eb106ec05 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PossibleContentsCaseRule.java @@ -9,18 +9,17 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; -import java.awt.*; +import java.awt.Point; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.*; -public class NumberForCellCaseRule extends CaseRule { +public class PossibleContentsCaseRule extends CaseRule { - public NumberForCellCaseRule() { - super("SKYS-CASE-0001", "Number For Cell", - "A blank cell must have height of 1 to n.", - "edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png"); + public PossibleContentsCaseRule() { + super("SKYS-CASE-0001", "Possible Contents", + "Each blank cell is could have height of 1 to n.", + "edu/rpi/legup/images/skyscrapers/PossibleContents.png"); } @Override @@ -53,27 +52,31 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { Set candidates = new HashSet(); for (int i = 1; i <= skyscrapersboard.getWidth(); i++) { - Board newCase = board.copy(); - PuzzleElement newCell = newCase.getPuzzleElement(puzzleElement); - newCell.setData(i); - newCase.addModifiedData(newCell); + candidates.add(i); + } - //if flags - boolean passed = true; - if(skyscrapersboard.getDupeFlag()){ - DuplicateNumberContradictionRule DupeRule = new DuplicateNumberContradictionRule(); - passed = passed && DupeRule.checkContradictionAt(newCase,newCell)!=null; - } - if(skyscrapersboard.getViewFlag()){ - PreemptiveVisibilityContradictionRule ViewRule = new PreemptiveVisibilityContradictionRule(); - passed = passed && ViewRule.checkContradictionAt(newCase,newCell)!=null; + for (int i = 0; i < skyscrapersboard.getWidth(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); + if (c.getType() == SkyscrapersType.Number) { + candidates.remove(c.getData()); } - //how should unresolved be handled? should it be? - if(passed){ - cases.add(newCase); + } + for (int i = 0; i < skyscrapersboard.getHeight(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); + if (c.getType() == SkyscrapersType.Number) { + candidates.remove(c.getData()); } } + Iterator it = candidates.iterator(); + while (it.hasNext()) { + Board case1 = board.copy(); + PuzzleElement data = case1.getPuzzleElement(puzzleElement); + data.setData(it.next()); + case1.addModifiedData(data); + cases.add(case1); + } + return cases; } @@ -90,9 +93,6 @@ public String checkRuleRaw(TreeTransition transition) { //System.out.println("0"); return "This case rule must have at least one child."; } - else if (childTransitions.size() != getCases(transition.getBoard(), childTransitions.get(0).getBoard().getModifiedData().iterator().next()).size()){ - return "Wrong number of children."; - } //TreeTransition case1 = childTransitions.get(0); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java deleted file mode 100644 index 1d96b3ed6..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java +++ /dev/null @@ -1,193 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.skyscrapers.SkyscrapersBoard; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - -import java.awt.*; -import java.util.Queue; -import java.util.LinkedList; -import java.util.List; - -public class PreemptiveVisibilityContradictionRule extends ContradictionRule { - - public PreemptiveVisibilityContradictionRule() { - super("SKYS-CONT-0006", "Preemptive Visibility", - "Visibility constraints are not met given an incomplete row/col", - "edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png"); - } - - /** - * Checks whether there is an instance of a visibility contradiction in every possible row/col based on the specific - * puzzleElement index using this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the all possible rows/cols contain a contradiction at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; - Point loc = cell.getLocation(); - - //Initialize instances of necessary contradiction and case rules - InsufficientVisibilityContradictionRule tooFew = new InsufficientVisibilityContradictionRule(); - ExceedingVisibilityContradictionRule tooMany = new ExceedingVisibilityContradictionRule(); - CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - - //Initialize skyscraperBoard queues for rows and cols - Queue rowQ = new LinkedList<>(); - rowQ.add(skyscrapersBoard); - Queue colQ = new LinkedList<>(); - colQ.add(skyscrapersBoard); - - // find all cases for the corresponding row and column for each possible skyscraper height - - //Add every possible case for all heights for each corresponding row and column - for(int i = 0; i < skyscrapersBoard.getWidth(); i++) { - int num = i + 1; - - //check row west clue - List rows; - - int size = rowQ.size(); - for(int j = 0; j < size; j++) { - SkyscrapersBoard temp = rowQ.poll(); //get row from the top of the stack - - //don't do anything if already in row - boolean exists = false; - for(SkyscrapersCell c : temp.getRowCol(loc.y,SkyscrapersType.Number,true)){ - if(c.getData()==num) { - exists = true; - break; - } - } - - if(exists) { - rowQ.add(temp); - } - else{ - //set flags - boolean dupeTemp = temp.getDupeFlag(); - boolean viewTemp = temp.getViewFlag(); - temp.setDupeFlag(false); - temp.setViewFlag(false); - - //get all cases for corresponding row based on west clue - rows = caseRule.getCasesFor(temp, skyscrapersBoard.getWestClues().get(loc.y), num); - - //reset flags - temp.setDupeFlag(dupeTemp); - temp.setViewFlag(viewTemp); - - //add all row cases to row queue - for (Board k : rows) { - rowQ.add((SkyscrapersBoard) k); - } - } - } - - //check col north clue - List cols; - - size = colQ.size(); - for(int j = 0; j < size; j++) { - SkyscrapersBoard temp = colQ.poll(); //get row from the top of the stack - - //don't do anything if already in col - boolean exists = false; - for(SkyscrapersCell c : temp.getRowCol(loc.x,SkyscrapersType.Number,false)){ - if(c.getData()==num) { - exists = true; - break; - } - } - - if(exists){ - colQ.add(temp); - } - else{ - //set flags - boolean dupeTemp = temp.getDupeFlag(); - boolean viewTemp = temp.getViewFlag(); - temp.setDupeFlag(false); - temp.setViewFlag(false); - - //get all cases for corresponding col based on north clue - cols = caseRule.getCasesFor(temp, skyscrapersBoard.getNorthClues().get(loc.x), num); - - //reset flags - temp.setDupeFlag(dupeTemp); - temp.setViewFlag(viewTemp); - - //add all row cases to row queue - for(Board k : cols) { - colQ.add((SkyscrapersBoard) k); - } - } - } - } - - String rowTooFew; - String rowTooMany; - boolean rowContradiction = true; - //check if each case board has a contradiction - while(rowQ.size()>0) { - SkyscrapersBoard fullRow = rowQ.poll(); - - //checks if there is a contradiction given the row based on the west clue - rowTooFew = tooFew.checkContradictionAt(fullRow, cell); // is cell the correct puzzle element to check? - rowTooMany = tooMany.checkContradictionAt(fullRow, cell); - - //boolean that checks if there is a contradiction within all rows - rowContradiction = rowContradiction && (rowTooFew == null || rowTooMany == null);// !null means there isn't a contradiction, so there must be a valid permutation of the array - } - - String colTooFew; - String colTooMany; - boolean colContradiction = true; - while(colQ.size()>0) { - SkyscrapersBoard fullCol = colQ.poll(); - - //checks if there is a contradiction given the col baesd on the north clue - colTooFew = tooFew.checkContradictionAt(fullCol, cell); - colTooMany = tooMany.checkContradictionAt(fullCol, cell); - - //boolean that checks if there is a contradiction within all the cols - colContradiction = colContradiction && (colTooFew == null || colTooMany == null); - } - - //if every possible permutation results in contradictions return null, else no contradiction - if(rowContradiction || colContradiction) { - return null; - } - return super.getNoContradictionMessage(); - } - - /** - * Checks whether the tree node has a contradiction using this rule - * - * @param board board to check contradiction - * @return null if the tree node contains a contradiction, otherwise error message - */ - @Override - public String checkContradiction(Board board) { - SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) - String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i,i)); - if (checkStr == null) { - return checkStr; - } - } - return "No instance of the contradiction " + this.ruleName + " here"; - } - - - -} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/Skyscrapers Ruleset.pdf b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/Skyscrapers Ruleset.pdf deleted file mode 100644 index 3ff35c2fe9eca703566e225aff879c8029337faa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194684 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 CQJawf diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/TODO.md b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/TODO.md index 94ef1e214..9ff730666 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/TODO.md +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/TODO.md @@ -1,22 +1,16 @@ ## TODO -spreadsheet : https://docs.google.com/spreadsheets/d/1l7aUZtavtysM8dtGnaEIXhBKMRGxekhnLIVoYIHYZi8/edit#gid=0 - - 1. Basic Rules: - - Come up with better names for 1Edge and FixedMax, they are now more general - 2. Contradiction Rules: - 3. Case Rules: - - Don't highlight cells when selecting a row/col? - - (override draw() in SkyscrapersElementView) - 4. Refactoring: - - document utility functions in the reference sheet, COMMENTS! - - review and identify dead code - - remove all these damn print statments (commented ones too if they aren't useful) - - Edit to allow blank clues - - Display flags somewhere - 5. Flags - - edit exporter to include flags in xml file format (if needed) - 6. Documentation - - UML diagram(s) - 7. Merge Skyscrapers to dev - 8. Add 5 more easy/med puzzles to skyscrapers \ No newline at end of file +1. Basic Rules: + - Last Cell + - Last Number + - 1-Edge + - N-Edge + - Fixed Max +2. Contradiction Rules: + - Duplicate Number + - Unresolved Cell + - Exceeding Visibility + - Insufficient Visibility +3. Case Rules: + - Possible Contents + - Possible Places diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java index c3abefd4d..98ed7e96d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java @@ -8,7 +8,6 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; import java.awt.*; -import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -17,7 +16,7 @@ public class UnresolvedCellContradictionRule extends ContradictionRule { public UnresolvedCellContradictionRule() { super("SKYS-CONT-0004", "Unresolved Cell", "Elimination leaves no possible number for a cell.", - "edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png"); + "edu/rpi/legup/images/skyscrapers/UnresolvedCell.png"); } /** @@ -30,14 +29,37 @@ public UnresolvedCellContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; + SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; + Point loc = cell.getLocation(); - NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); - ArrayList cases = caseRule.getCases(board,puzzleElement); + Set candidates = new HashSet(); - if(cases.size()==0){ - return null; + //check row + for (int i = 0; i < skyscrapersboard.getWidth(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); + if (i != loc.x && cell.getType() == SkyscrapersType.UNKNOWN && c.getType() == SkyscrapersType.Number) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + candidates.add(c.getData()); + } + } + + // check column + for (int i = 0; i < skyscrapersboard.getHeight(); i++) { + SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); + if (i != loc.y && cell.getType() == SkyscrapersType.UNKNOWN && c.getType() == SkyscrapersType.Number) { + //System.out.print(c.getData()); + //System.out.println(cell.getData()); + candidates.add(c.getData()); + } } + if (candidates.size() == skyscrapersboard.getWidth()) { + System.out.print("violation"); + return null; + } + //System.out.print("Does not contain a contradiction at this index"); return super.getNoContradictionMessage(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java deleted file mode 100644 index e3b27a777..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java +++ /dev/null @@ -1,97 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - - -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.skyscrapers.SkyscrapersBoard; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - -import java.awt.*; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -public class UnresolvedNumberContradictionRule extends ContradictionRule { - - public UnresolvedNumberContradictionRule() { - super("SKYS-CONT-0005", "Unresolved Number", - "No possible cell for a number without a duplicate contradiction.", - //specify a number? defaulting to every number for now. expand to more than duplicate? - "edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png"); - } - - /** - * Checks whether the transition has a contradiction at the specific 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 - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; - Point loc = cell.getLocation(); - - CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - for(int i=0;i Student 1 - * | | - * | | -> Proofs - */ - - LegupPreferences preferences = LegupPreferences.getInstance(); - File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); - JFileChooser folderBrowser = new JFileChooser(preferredDirectory); - - - folderBrowser.setCurrentDirectory(new File(LegupPreferences.WORK_DIRECTORY)); - folderBrowser.setDialogTitle("Select Directory"); - folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderBrowser.setAcceptAllFileFilterUsed(false); - folderBrowser.showOpenDialog(this); - folderBrowser.setVisible(true); - File folder = folderBrowser.getSelectedFile(); - - File resultFile = new File(folder.getAbsolutePath() + File.separator +"result.csv"); - try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { - writer.append("Name"); - writer.append(","); - writer.append("File Name"); - writer.append(","); - writer.append("Solved or not"); - writer.append("\n"); - //csvWriter.flush(); - //csvWriter.close(); - - for (final File folderEntry : folder.listFiles(File::isDirectory)) { - writer.append(folderEntry.getName()); - writer.append(","); - int count1 = 0; - for (final File fileEntry : folderEntry.listFiles()) { - if (fileEntry.getName().charAt(0) == '.'){ - continue; - } - count1++; - if (count1 > 1){ - writer.append(folderEntry.getName()); - writer.append(","); - } - writer.append(fileEntry.getName()); - writer.append(","); - String fileName = folderEntry.getAbsolutePath() + File.separator + fileEntry.getName(); - System.out.println("This is path "+fileName); - File puzzleFile = new File(fileName); - if (puzzleFile != null && puzzleFile.exists()) { - try { - legupUI.displayPanel(1); - legupUI.getProofEditor(); - GameBoardFacade.getInstance().loadPuzzle(fileName); - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - legupUI.setTitle(puzzleName + " - " + puzzleFile.getName()); - facade = GameBoardFacade.getInstance(); - Puzzle puzzle = facade.getPuzzleModule(); - if (puzzle.isPuzzleComplete()) { - writer.append("Solved"); - System.out.println(fileEntry.getName() + " solved"); - } - else { - writer.append("Not solved"); - System.out.println(fileEntry.getName() + " not solved"); - } - writer.append("\n"); - } - catch (InvalidFileFormatException e) { - LOGGER.error(e.getMessage()); - } - } - } - if (count1 == 0){ - writer.append("No file"); - writer.append("\n"); - } - } - } - catch (IOException ex){ - LOGGER.error(ex.getMessage()); - - this.buttons[3].addActionListener((ActionEvent e) -> checkProofAll()); - - } } - /* - This function is use to check each function xml proof file have the flag = true. - Also, we will go to check each file is xml file or not which we have 3 result solve, unsolve and ungradeable - */ - public void use_xml_to_check() throws Exception{ - 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 - */ - - LegupPreferences preferences = LegupPreferences.getInstance(); - File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); - JFileChooser folderBrowser = new JFileChooser(preferredDirectory); - - - folderBrowser.setCurrentDirectory(new File(LegupPreferences.WORK_DIRECTORY)); - folderBrowser.setDialogTitle("Select Directory"); - folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderBrowser.setAcceptAllFileFilterUsed(false); - folderBrowser.showOpenDialog(this); - folderBrowser.setVisible(true); - File folder = folderBrowser.getSelectedFile(); - - File resultFile = new File(folder.getAbsolutePath() + File.separator +"result.csv"); - SAXParserFactory spf = SAXParserFactory.newInstance(); - SAXParser saxParser = spf.newSAXParser(); -// String path = "C:\\Users\\LiWeiJun\\Desktop\\TestSet\\TestSet\\roseh"; - //read the xml file - try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { - writer.write("Name"); - writer.write(","); - writer.write("File Name"); - writer.write(","); - writer.write("Solved or not"); - writer.write("\n"); - for (final File folderEntry : folder.listFiles(File::isDirectory)) { - writer.write(folderEntry.getName()); - writer.write(","); - int count1 = 0; - for (final File fileEntry : folderEntry.listFiles()) { - if (fileEntry.getName().charAt(0) == '.') { - continue; - } - count1++; - if (count1 > 1) { - writer.write(folderEntry.getName()); - writer.write(","); - } - writer.write(fileEntry.getName()); - writer.write(","); - String path =folderEntry.getAbsolutePath() + File.separator + fileEntry.getName(); - System.out.println(path); - boolean is_xml_file = isxmlfile(fileEntry); - if(is_xml_file){ - saxParser.parse(path, new DefaultHandler(){ - @Override - public void startDocument() throws SAXException { - System.out.println("start doc"); - } - - @Override - public void endDocument() throws SAXException { - System.out.println("end doc"); - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - int length = attributes.getLength(); - for (int i = 0 ;i Student 1 - * | | - * | | -> Proofs - */ - - LegupPreferences preferences = LegupPreferences.getInstance(); - File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); - JFileChooser folderBrowser = new JFileChooser(preferredDirectory); - - - folderBrowser.setCurrentDirectory(new File(LegupPreferences.WORK_DIRECTORY)); - folderBrowser.setDialogTitle("Select Directory"); - folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderBrowser.setAcceptAllFileFilterUsed(false); - folderBrowser.showOpenDialog(this); - folderBrowser.setVisible(true); - - 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(); - traverseDir1(folderEntry, writer, path); - } - } - catch (IOException ex) { - LOGGER.error(ex.getMessage()); - } - JOptionPane.showMessageDialog(null, "Batch grading complete."); - } - public void traverseDir1(File folder, BufferedWriter writer, String path) throws IOException{ - 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()) { - traverseDir1(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"); - } - } - } private void initText() { // Note: until an auto-changing version label is implemented in the future, I removed // the version text from the home screen to avoid confusion @@ -569,7 +189,6 @@ private void render() { 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]); @@ -584,105 +203,6 @@ private void openNewPuzzleDialog() { cpd.setVisible(true); } - private void checkProofAll() { - /* - * Select dir to grade; recursively grade sub-dirs using traverseDir() - * Selected dir must have sub-dirs for each student: - * GradeThis - * | - * | -> Student 1 - * | | - * | | -> Proofs - */ - - LegupPreferences preferences = LegupPreferences.getInstance(); - File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); - folderBrowser = new JFileChooser(preferredDirectory); - - folderBrowser.showOpenDialog(this); - folderBrowser.setVisible(true); - folderBrowser.setCurrentDirectory(new File(LegupPreferences.WORK_DIRECTORY)); - 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 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 void openEditorWithNewPuzzle(String game, int rows, int columns) throws IllegalArgumentException { // Validate the dimensions GameBoardFacade facade = GameBoardFacade.getInstance(); diff --git a/src/main/java/edu/rpi/legup/ui/LegupUI.java b/src/main/java/edu/rpi/legup/ui/LegupUI.java index 4a25921b7..f12f45a70 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupUI.java +++ b/src/main/java/edu/rpi/legup/ui/LegupUI.java @@ -14,6 +14,8 @@ import edu.rpi.legup.ui.lookandfeel.LegupLookAndFeel; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; +import edu.rpi.legupupdate.Update; +import edu.rpi.legupupdate.UpdateProgress; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java index 289f0a0f7..177fa183c 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -17,7 +17,9 @@ 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.TreeView; 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; @@ -25,7 +27,6 @@ import javax.swing.border.TitledBorder; import java.awt.*; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.io.BufferedWriter; import java.io.File; @@ -49,7 +50,7 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private JButton[] toolBarButtons; private JMenu file; - private JMenuItem newPuzzle, resetPuzzle, saveProofAs,saveProofChange,helpTutorial, preferences, exit; + private JMenuItem newPuzzle, resetPuzzle, saveProof, preferences, exit; private JMenu edit; private JMenuItem undo, redo, fitBoardToScreen, fitTreeToScreen; @@ -114,10 +115,8 @@ public JMenuBar getMenuBar() { newPuzzle = new JMenuItem("Open"); resetPuzzle = new JMenuItem("Reset Puzzle"); // genPuzzle = new JMenuItem("Puzzle Generators"); - saveProofAs = new JMenuItem("Save Proof As"); // create a new file to save - saveProofChange = new JMenuItem("Save Proof Change"); // save to the current file + saveProof = new JMenuItem("Save Proof"); preferences = new JMenuItem("Preferences"); - helpTutorial = new JMenuItem("Help"); // jump to web page exit = new JMenuItem("Exit"); edit = new JMenu("Edit"); @@ -243,51 +242,21 @@ public JMenuBar getMenuBar() { } file.addSeparator(); - file.add(saveProofAs); - saveProofAs.addActionListener((ActionEvent) -> saveProofAs()); - - - //save proof as - if (os.equals("mac")) { - saveProofAs.setAccelerator(KeyStroke.getKeyStroke('S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { - saveProofAs.setAccelerator(KeyStroke.getKeyStroke('S', InputEvent.CTRL_DOWN_MASK)); - } - - // save proof change + file.add(saveProof); + saveProof.addActionListener((ActionEvent) -> saveProof()); if (os.equals("mac")) { - saveProofChange.setAccelerator(KeyStroke.getKeyStroke('A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + saveProof.setAccelerator(KeyStroke.getKeyStroke('S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); } else { - saveProofChange.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.CTRL_DOWN_MASK)); + saveProof.setAccelerator(KeyStroke.getKeyStroke('S', InputEvent.CTRL_DOWN_MASK)); } - - file.add(saveProofChange); - saveProofChange.addActionListener((ActionEvent) -> saveProofChange()); - file.addSeparator(); - - // preference file.add(preferences); preferences.addActionListener(a -> { PreferencesDialog preferencesDialog = new PreferencesDialog(this.frame); }); file.addSeparator(); - // help function - if (os.equals("mac")) { - helpTutorial.setAccelerator(KeyStroke.getKeyStroke('H', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { - helpTutorial.setAccelerator(KeyStroke.getKeyStroke('H', InputEvent.CTRL_DOWN_MASK)); - } - file.add(helpTutorial); - helpTutorial.addActionListener((ActionEvent) -> helpTutorial()); - file.addSeparator(); - - - //exit file.add(exit); exit.addActionListener((ActionEvent) -> this.legupUI.displayPanel(0)); if (os.equals("mac")) { @@ -347,7 +316,6 @@ public JMenuBar getMenuBar() { return mBar; } - // File opener public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { @@ -356,21 +324,19 @@ public Object[] promptPuzzle() { } } - if (fileDialog == null) { - fileDialog = new FileDialog(this.frame); - } LegupPreferences preferences = LegupPreferences.getInstance(); - String preferredDirectory = preferences.getUserPref(LegupPreferences.WORK_DIRECTORY); + File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); + folderBrowser = new JFileChooser(preferredDirectory); + folderBrowser.setDialogTitle("Select Proof File"); + folderBrowser.showOpenDialog(this); + folderBrowser.setFileSelectionMode(JFileChooser.FILES_ONLY); + folderBrowser.setAcceptAllFileFilterUsed(true); + folderBrowser.setVisible(true); - fileDialog.setMode(FileDialog.LOAD); - fileDialog.setTitle("Select Proof File"); - fileDialog.setDirectory(preferredDirectory); - fileDialog.setVisible(true); String fileName = null; - File puzzleFile = null; - - if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { - fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); + File puzzleFile = folderBrowser.getSelectedFile(); + if (folderBrowser.getCurrentDirectory() != null && folderBrowser.getSelectedFile().getName() != null) { + fileName = puzzleFile.getAbsolutePath() + File.separator; puzzleFile = new File(fileName); } @@ -395,49 +361,25 @@ public void loadPuzzle(String fileName, File puzzleFile) { 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); - loadPuzzle(); } else { JOptionPane.showMessageDialog(null, "File does not exist or it cannot be read", "Error", JOptionPane.ERROR_MESSAGE); - loadPuzzle(); } } } } /** - * direct Saves the current prdoof in current file + * Saves a proof */ - private void direct_save(){ - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - if (puzzle == null) { - return; - } - String fileName = GameBoardFacade.getInstance().getCurFileName(); - 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(); - } - } - } - /** - * Create a new file and save proof to it - */ - private void saveProofAs() { + private void saveProof() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { return; } fileDialog.setMode(FileDialog.SAVE); - fileDialog.setTitle("Save Proof As"); + fileDialog.setTitle("Save Proof"); String curFileName = GameBoardFacade.getInstance().getCurFileName(); if (curFileName == null) { fileDialog.setDirectory(LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); @@ -467,63 +409,6 @@ private void saveProofAs() { } } - // Hyperlink for help button; links to wiki page for tutorials - private void helpTutorial() { - - Runtime rt = Runtime.getRuntime(); - String url = "https://github.com/Bram-Hub/Legup/wiki/LEGUP-Tutorial"; // empty page 2022 Fall semester - try{ - //rt.exec("rundll32 url.dll,FileProtocolHandler "+url); - java.awt.Desktop.getDesktop().browse(java.net.URI.create(url)); - } - catch(IOException e){ - e.printStackTrace(); - } - } - //add the new function need to implement - public void add_drop(){ - // add the mouse event then we can use the new listener to implement and - // we should create a need jbuttom for it to ship the rule we select. - JPanel panel= new JPanel(); - JButton moveing_buttom= new JButton(); - moveing_buttom.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - //get the selected rule - } - }); - panel.add(moveing_buttom); - - } - - - - - // Quick save proof to the current file with a pop window to show "successfully saved" - private void saveProofChange(){ - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - if (puzzle == null) { - return; - } - String fileName = GameBoardFacade.getInstance().getCurFileName(); - if (fileName != null) { - try { - PuzzleExporter exporter = puzzle.getExporter(); - if (exporter == null) { - throw new ExportFileException("Puzzle exporter null"); - } - exporter.exportPuzzle(fileName); - // Save confirmation - JOptionPane.showMessageDialog(null, "Successfully Saved","Confirm",JOptionPane.INFORMATION_MESSAGE); - } - 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); @@ -710,8 +595,6 @@ public void setPuzzleView(Puzzle puzzle) { ruleFrame.getBasicRulePanel().setRules(puzzle.getBasicRules()); ruleFrame.getCasePanel().setRules(puzzle.getCaseRules()); ruleFrame.getContradictionPanel().setRules(puzzle.getContradictionRules()); - //ruleFrame.getSearchPanel().setRules(puzzle.getContradictionRules()); - ruleFrame.getSearchPanel().setSearchBar(puzzle); toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); @@ -922,6 +805,30 @@ public void onUndo(boolean isBottom, boolean isTop) { } } + /** + * 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); } diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java index 7126e3477..9f65f9119 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -103,17 +103,8 @@ public void setMenuBar() { newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); } // file>save - JMenuItem savePuzzle = new JMenuItem("Save Proof As"); + JMenuItem savePuzzle = new JMenuItem("Save"); savePuzzle.addActionListener((ActionEvent) -> savePuzzle()); - JMenuItem directSavePuzzle = new JMenuItem("Direct Save Proof "); - directSavePuzzle.addActionListener((ActionEvent) -> direct_save()); - if (os.equals("mac")) { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); - } - JMenuItem exit = new JMenuItem("Exit"); exit.addActionListener((ActionEvent) -> this.legupUI.displayPanel(0)); if (os.equals("mac")) { @@ -124,7 +115,6 @@ public void setMenuBar() { } menus[0].add(newPuzzle); menus[0].add(savePuzzle); - menus[0].add(directSavePuzzle); menus[0].add(exit); // EDIT @@ -246,7 +236,6 @@ public void loadPuzzleFromHome(String game, int rows, int columns) throws Illega } } - // File opener public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { @@ -257,12 +246,8 @@ public Object[] promptPuzzle() { if (fileDialog == null) { fileDialog = new FileDialog(this.frame); } - LegupPreferences preferences = LegupPreferences.getInstance(); - String preferredDirectory = preferences.getUserPref(LegupPreferences.WORK_DIRECTORY); - fileDialog.setMode(FileDialog.LOAD); fileDialog.setTitle("Select Puzzle"); - fileDialog.setDirectory(preferredDirectory); fileDialog.setVisible(true); String fileName = null; File puzzleFile = null; @@ -290,8 +275,6 @@ public void loadPuzzle(String fileName, File puzzleFile) { } catch (InvalidFileFormatException e) { LOGGER.error(e.getMessage()); - JOptionPane.showMessageDialog(null, "File does not exist or it cannot be read", "Error", JOptionPane.ERROR_MESSAGE); - loadPuzzle(); } } } @@ -363,26 +346,6 @@ public void setPuzzleView(Puzzle puzzle) { /** * Saves a puzzle */ - - private void direct_save(){ - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - if (puzzle == null) { - return; - } - String fileName = GameBoardFacade.getInstance().getCurFileName(); - 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(); - } - } - } private void savePuzzle() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -428,6 +391,4 @@ public DynamicView getDynamicBoardView() { return dynamicBoardView; } - - } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java index 7e2e30bf5..12cae7de1 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java @@ -13,7 +13,7 @@ public class RuleButton extends JButton { * @param rule rule to create the button */ RuleButton(Rule rule) { - super(rule.getRuleName(), rule.getImageIcon()); // display rules' name under rule when load the icon + super(rule.getImageIcon()); this.rule = rule; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java index d3e3799f9..dd60f8dc3 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java @@ -9,8 +9,13 @@ import java.awt.BorderLayout; import java.awt.Dimension; -import javax.swing.*; +import javax.swing.ButtonGroup; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.BorderFactory; import javax.swing.border.TitledBorder; public class RuleFrame extends JPanel { @@ -23,8 +28,6 @@ public class RuleFrame extends JPanel { private ContradictionRulePanel contradictionPanel; private CaseRulePanel casePanel; - private SearchBarPanel searchPanel; - private JTabbedPane tabbedPane; private JLabel status; private ButtonGroup buttonGroup; @@ -63,11 +66,6 @@ protected boolean shouldRotateTabRuns(int i) { newp.getVerticalScrollBar().setUnitIncrement(16); tabbedPane.addTab(contradictionPanel.name, contradictionPanel.icon, newp, contradictionPanel.toolTip); - searchPanel = new SearchBarPanel(this); - JScrollPane newsp = new JScrollPane(searchPanel); - newsp.getVerticalScrollBar().setUnitIncrement(16); - tabbedPane.addTab(searchPanel.name, searchPanel.icon, newsp, searchPanel.toolTip); - setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); setPreferredSize(new Dimension(330, 256)); @@ -93,7 +91,6 @@ public void setSelectionByRule(Rule rule) { basicRulePanel.setSelectionByRule(rule); casePanel.setSelectionByRule(rule); contradictionPanel.setSelectionByRule(rule); - } /** @@ -139,7 +136,6 @@ public void setRules(Puzzle puzzle) { basicRulePanel.setRules(puzzle.getBasicRules()); contradictionPanel.setRules(puzzle.getContradictionRules()); casePanel.setRules(puzzle.getCaseRules()); - } /** @@ -175,7 +171,4 @@ public CaseRulePanel getCasePanel() { public ContradictionRulePanel getContradictionPanel() { return contradictionPanel; } - public SearchBarPanel getSearchPanel() { - return searchPanel; - } } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java index 78a07cdda..c6bc2ee0a 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java @@ -1,13 +1,10 @@ package edu.rpi.legup.ui.proofeditorui.rulesview; -import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.ui.WrapLayout; -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import javax.swing.ImageIcon; +import javax.swing.JPanel; import java.util.ArrayList; import java.util.List; @@ -17,8 +14,6 @@ public abstract class RulePanel extends JPanel { protected String toolTip; protected RuleButton[] ruleButtons; - protected JPanel searchBarPanel; - JTextField textField; protected RuleFrame ruleFrame; protected List rules; @@ -55,189 +50,16 @@ public void setRules(List rules) { for (int i = 0; i < rules.size(); i++) { Rule rule = rules.get(i); - ruleButtons[i] = new RuleButton(rule); - ruleButtons[i].setPreferredSize(new Dimension(150,150));// adjust the size of each RuleButton - ruleButtons[i].setHorizontalTextPosition(JButton.CENTER); - ruleButtons[i].setVerticalTextPosition(JButton.BOTTOM); - ruleFrame.getButtonGroup().add(ruleButtons[i]); - ruleButtons[i].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); // showing description + + ruleButtons[i].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); ruleButtons[i].addActionListener(ruleFrame.getController()); add(ruleButtons[i]); - } revalidate(); } - - /** - * Search a certain rule in all the puzzles and set it for the searchBarPanel - * - * @param puzzle, ruleName - * - * This function is the searching algorithm for "public void setSearchBar(Puzzle allPuzzle)" (below) - * - * It takes two param Puzzle puzzle and String ruleName - * puzzle contains rules, this function will compare each rule of puzzle with ruleName, - * to find exact same, similar rules, or all the rules with same start letter (if input is a signal letter) - */ - public void searchForRule(Puzzle puzzle, String ruleName) { - - List> allrules = new ArrayList>(3); - allrules.add(0, puzzle.getBasicRules()); - allrules.add(1, puzzle.getCaseRules()); - allrules.add(2, puzzle.getContradictionRules()); - - ruleButtons = new RuleButton[100]; - int similarfound = 0; - - - for(int i = 0; i < allrules.size(); i++) { - for (int j = 0; j < allrules.get(i).size(); j++) { - Rule rule = allrules.get(i).get(j); - if ((ruleName).equals(rule.getRuleName().toUpperCase())) { - - ruleButtons[0] = new RuleButton(rule); - ruleFrame.getButtonGroup().add(ruleButtons[0]); - - ruleButtons[0].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); - ruleButtons[0].addActionListener(ruleFrame.getController()); - add(ruleButtons[0]); - revalidate(); - return; - - } - else if(similarityCheck(ruleName, rule.getRuleName().toUpperCase())>0.2){ - ruleButtons[similarfound] = new RuleButton(rule); - ruleFrame.getButtonGroup().add(ruleButtons[similarfound]); - - ruleButtons[similarfound].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); - ruleButtons[similarfound].addActionListener(ruleFrame.getController()); - add(ruleButtons[similarfound]); - similarfound+=1; - revalidate(); - } - else if((ruleName.charAt(0)) == (rule.getRuleName().toUpperCase()).charAt(0)){ - ruleButtons[similarfound] = new RuleButton(rule); - ruleFrame.getButtonGroup().add(ruleButtons[similarfound]); - - ruleButtons[similarfound].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); - ruleButtons[similarfound].addActionListener(ruleFrame.getController()); - add(ruleButtons[similarfound]); - similarfound+=1; - revalidate(); - } - } - } - - if(ruleButtons[0] == null) { - JOptionPane.showMessageDialog(null, "Please input the correct rule name", "Confirm", JOptionPane.INFORMATION_MESSAGE); - } - } - - /** - * Calculates the similarity (a number within 0 and 1) between two strings. - * This funtion will take two para String s1 and String s2, which s1 is the user's input - * and s2 is the compared really rule name - * - * similarityCheck will use a helper function to calculate a similarity degree(from 0 to 1). - * closer to 0 means less similar, and closer to 1 means more similar. - */ - public static double similarityCheck(String s1, String s2) { - String longer = s1, shorter = s2; - if (s1.length() < s2.length()) { // longer should always have greater length - longer = s2; shorter = s1; - } - int longerLength = longer.length(); - if (longerLength == 0) { - return 1.0; /* both strings are zero length */ - } - return (longerLength - editDistance(longer, shorter)) / (double) longerLength; - } - /** - * Help function for similarityCheck(); - */ - public static int editDistance(String s1, String s2) { - s1 = s1.toLowerCase(); - s2 = s2.toLowerCase(); - - int[] costs = new int[s2.length() + 1]; - for (int i = 0; i <= s1.length(); i++) { - int lastValue = i; - for (int j = 0; j <= s2.length(); j++) { - if (i == 0) { - costs[j] = j; - } - else { - if (j > 0) { - int newValue = costs[j - 1]; - if (s1.charAt(i - 1) != s2.charAt(j - 1)) { - newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1; - } - costs[j - 1] = lastValue; - lastValue = newValue; - } - } - } - if (i > 0) { - costs[s2.length()] = lastValue; - } - } - return costs[s2.length()]; - } - - /** - * Sets the search bar for SearchBarPanel - * search bar allows user to input a name to get relative rules - * once a name is entered and click ok will load (a/several) rule icon, - * which has all the functions just as other rule icons. - */ - public void setSearchBar(Puzzle allPuzzle){ - - searchBarPanel = new JPanel(new FlowLayout(SwingConstants.LEADING, 6, 6)); - add(searchBarPanel); - JLabel findLabel = new JLabel("Search:"); - searchBarPanel.add(findLabel); - searchBarPanel.add(Box.createRigidArea(new Dimension(1, 0))); - textField = new JTextField(8); - searchBarPanel.add(textField); - searchBarPanel.add(Box.createRigidArea(new Dimension(1, 0))); - JButton findButton = new JButton("Go"); - findButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - if (ruleButtons != null) { - for(int i = 0; i != ruleButtons.length; i++) { - if(ruleButtons[i]==null){ - continue; - } - ruleButtons[i].removeActionListener(ruleFrame.getController()); - } - } - String inputRule = textField.getText().toUpperCase().trim(); - - if (!inputRule.isEmpty()) { - if (ruleButtons != null) { - - for (int x = 0; x < ruleButtons.length; ++x) { - if(ruleButtons[x]==null){ - continue; - } - remove(ruleButtons[x]); - } - } - searchForRule(allPuzzle, inputRule); - } - else { - JOptionPane.showMessageDialog(null, "Please give a name","Confirm",JOptionPane.INFORMATION_MESSAGE); - } - - } - }); - searchBarPanel.add(findButton); - } - /** * Sets the selection by the specified rule * diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java deleted file mode 100644 index 2c32b6e88..000000000 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java +++ /dev/null @@ -1,19 +0,0 @@ -package edu.rpi.legup.ui.proofeditorui.rulesview; - -import javax.swing.*; - -public class SearchBarPanel extends RulePanel { - /** - * SearchBarPanel Constructor creates a SearchBarPanel - * - * @param ruleFrame rule frame that this SearchBarPanel is contained in - * - * This class is used to create a panel named "search bar" - */ - SearchBarPanel(RuleFrame ruleFrame) { - super(ruleFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Zoom In.png")); - this.name = "Search Rules"; - this.toolTip = "Search Rules"; - } -} 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 index dfe879379..037782225 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java @@ -4,18 +4,14 @@ import edu.rpi.legup.model.rules.Rule; import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; public class ElementButton extends JButton { private Element element; - private final Border originalBorder; ElementButton(Element e) { super(e.getImageIcon()); this.element = e; - this.originalBorder = this.getBorder(); } public Element getElement() { @@ -25,14 +21,4 @@ public Element getElement() { public void setElement(Element e) { this.element = e; } - - public void setBorderToSelected() { - Border newBorderIn = BorderFactory.createLineBorder(new Color(20, 140, 70), 2, true); - Border newBorder = BorderFactory.createCompoundBorder(newBorderIn, this.originalBorder); - this.setBorder(newBorder); - } - - public void resetBorder() { - this.setBorder(this.originalBorder); - } } 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 index 1fc2b3792..3316379df 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.EditorElementController; import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.ui.lookandfeel.components.MaterialTabbedPaneUI; import javax.swing.*; import javax.swing.border.TitledBorder; @@ -23,28 +22,16 @@ public class ElementFrame extends JPanel { public ElementFrame(EditorElementController controller) { this.controller = controller; - MaterialTabbedPaneUI tabOverride = new MaterialTabbedPaneUI() { - //this prevents the tabs from moving around when you select them - @Override - protected boolean shouldRotateTabRuns(int i) { - return false; - } - }; this.tabbedPane = new JTabbedPane(); - tabbedPane.setUI(tabOverride); JLabel status = new JLabel("", SwingConstants.CENTER); this.buttonGroup = new ButtonGroup(); nonPlaceableElementPanel = new NonPlaceableElementPanel(this); - //nonPlaceableElementPanel.setMinimumSize(new Dimension(100,200)); tabbedPane.addTab(nonPlaceableElementPanel.getName(), nonPlaceableElementPanel.getIcon(), new JScrollPane(nonPlaceableElementPanel), nonPlaceableElementPanel.getToolTip()); placeableElementPanel = new PlaceableElementPanel(this); - //placeableElementPanel.setMinimuSize(new Dimension(100,200)); tabbedPane.addTab(placeableElementPanel.getName(), placeableElementPanel.getIcon(), new JScrollPane(placeableElementPanel), placeableElementPanel.getToolTip()); - tabbedPane.setTabPlacement(JTabbedPane.TOP); - setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); diff --git a/src/main/java/edu/rpi/legup/user/Submission.java b/src/main/java/edu/rpi/legup/user/Submission.java new file mode 100644 index 000000000..eb2b8446c --- /dev/null +++ b/src/main/java/edu/rpi/legup/user/Submission.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.user; + +import edu.rpi.legup.model.gameboard.Board; + +public class Submission { + public Submission(Board board) { + + } + + public void submit() { + + } +} diff --git a/src/main/java/edu/rpi/legup/user/UsageStatistics.java b/src/main/java/edu/rpi/legup/user/UsageStatistics.java new file mode 100644 index 000000000..192db593f --- /dev/null +++ b/src/main/java/edu/rpi/legup/user/UsageStatistics.java @@ -0,0 +1,63 @@ +package edu.rpi.legup.user; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class UsageStatistics { + + private static final String url = "https://legup-3b4a5.firebaseio.com/databases/test.json"; + + public UsageStatistics() { + + } + + public boolean sendErrorReport() { + try { + HttpClient httpclient = new DefaultHttpClient(); + + HttpPost httppost = new HttpPost(url); + + // Request parameters and other properties. + httppost.setEntity(new StringEntity("{\"test\": \"jeff\"}")); +// List params = new ArrayList<>(2); +// params.add(new BasicNameValuePair("param-1", "12345")); +// params.add(new BasicNameValuePair("param-2", "Hello!")); +// httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); + + //Execute and get the response. + HttpResponse response = httpclient.execute(httppost); + HttpEntity entity = response.getEntity(); + + if (entity != null) { + InputStream instream = entity.getContent(); + + try { +// System.err.println(new String(instream.readAllBytes())); + } + finally { + instream.close(); + } + } + } + catch (IOException e) { + return false; + } + return false; + } + + public static void main(String[] args) { + new UsageStatistics().sendErrorReport(); + } +} diff --git a/src/main/java/edu/rpi/legup/user/User.java b/src/main/java/edu/rpi/legup/user/User.java new file mode 100644 index 000000000..2e542a96c --- /dev/null +++ b/src/main/java/edu/rpi/legup/user/User.java @@ -0,0 +1,5 @@ +package edu.rpi.legup.user; + +public class User { + +} diff --git a/src/main/resources/edu/rpi/legup/images/skyscraper/DuplicateNumber.png b/src/main/resources/edu/rpi/legup/images/skyscraper/DuplicateNumber.png new file mode 100644 index 0000000000000000000000000000000000000000..64fb4fc865c281f533be041dc6aad136e9e208ac GIT binary patch literal 2413 zcmb_d2~bm67EM?>h^T-dw&I4OECCu&qmhK&h(bsMvTImOr)_Z{fe*@m#h2bZt~B9I4g)4>@8ktqN% zhXM+;JecD^IszX|qq8}@agk_2rWdrNJA*_ma?3(Xu$GKQ;Q|0fhMei+pmv<@3}PZ% z1dzdKl0G~<4AwT0LGJwgd_sIY00>f3Q#llb%!4lAm3Sa1oNf3OXF$ZESR#mQHW2}c zxak-zj}qPjuLKWTLJ;H@k$_1AWsq%n*)+{k%aXzoeQTG(0f3JmKW1iT78DeKl%Iq0 zWx*}X1?vj7_5Xe#t_$b-!1}8~j(CNF0Vx~M*;AQEArM%BgPpb8g_F}oXR-$EG?Fld z>T0DAbG!+q@iaN3Yu?J9#{4z|O+_jI1Wt7y%BmPB>UE%x5Bqb)r2~`ZBJ{9-_;}_< z1d89jzcUdcQMq?1=&4G#7ISDUSKw5|T>P7#Z}g_dSW<*lDZK0&x?>bM)<_0!*cKSq z;EZdVmo$8iRR^6cbNv|pIeO=s;_;zW(&!bBq|xG5I@77D1Pg?~!UL5@<<4VLycq9P zOk}FKOCOu`BNthsst`gJLgP&<+Q6a|nnZR4uI04*2m&r(w6I%lL`grh1b!Dd=*R+O zWK*r_*r?r5PTw>OA+I7DD&%fVjBuh=3fm0Ft)IzX5E@(^j~;s+B=s7q=#bz zG+|=81|K>wtF(sWwgP=N^fsGsxn@M<=db-W*IeWXd*9_B>2+YKH_WQ>`eh>0uQ4)P zi0jz=*7W#*%Un#o&etK->u2PlENaG+;v-WJNb8I24Vy{pw|JnfgB@)mMQG%P2U31i z7~FL-L#h?3h@!X!4NM$V7?}T`sY_k5MzC zq>0h!uB3Isr@IW&a3s42S82Btglb;ydWNy>eLs7+lM<*qse~x6DIDqX9CgktXS;(x-2{O&!@H47{d99ba0b_XG2khf%|A9ZNYkS& zGl`kA$#BE$g@>tUla6<1Ab0z!=jmie6%wjtosbjig6p-}(jxM+OQLd-5<&3OWEVO$ z+K(Zv1}=$LN#LbZui;B({X^m&%}dQ@_;U7p-0Qs;*Jeml7Pmgq4JbU!-Jvd+nN9-h zsC2VoP3m(`EUdWdUCqzT;DxXWPwcLdXA-rGX*kK*TrJ~lzxf5bll(XK_{$osV z&thMWSoV}mDB8Sado|WMm9qmG(rsdsi`!*Q;_sdQEa}k%#sP&qtszY+`ScWWjEa$U z?U-|1yL4owt^+AoLa;w=D_s2T+y34>VAC1prrlTV`?VqYY!bDOy3-9LD`_qWQ|cQ` z43BPB<}$+xniv^T&0wZ{B-x#?+^~{>M^X~1*W2G*qjy%%qUF_!O)|pUqB(c)9E3FD z?a~c>Tr9j>k!zbm;bnTS)!lmX*e?gO?_V~$MPzwOPMbF74%T_XQ_>i^ceQQ)NtSZP z!3d^G{}DXlrP;?`-n72;!pqe#c^%ceepU))8&r}*Sl4o&(?NKscNA72fE6W z$=J<-^1c-=)Z<~k%b)AH__M~o(h>R{Q~d53A@bm;PS)gGe$d;k?{q+4okSF05WWz} zHwRys5Aai?*J<6#4$#zReD!0Hfs9YZ`L4Y`m@j99mJ2!#V+|@j?cCo{2HP>&GWff>n0hi0<<9ZvP!xGYiifewZ(zqI{dYKh_Fvj!l`!HRF}O~r5DXu_4mhJDnj+}QQ(zjPEJcAuG=%Sm3pT#u;A$J260X!36`ePCs=e2g3_=unwQ)Q4Kb%P3WS$2T{ADH77x06= Z!k#nZ(snP>&VW}p#K9hG$FT8>`#a>+vu^+Z literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/skyscraper/LastNumber.png b/src/main/resources/edu/rpi/legup/images/skyscraper/LastNumber.png new file mode 100644 index 0000000000000000000000000000000000000000..1c219e3281cae048f73668ebffe66ab498201159 GIT binary patch literal 28799 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 DOe;ay literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/skyscraper/PossibleContents.png b/src/main/resources/edu/rpi/legup/images/skyscraper/PossibleContents.png new file mode 100644 index 0000000000000000000000000000000000000000..7eaee5d06bb81e453b84ba30a3a29b969203935e GIT binary patch literal 67322 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
  • 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 E4_Nys#sB~S literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png deleted file mode 100644 index 7ab0cdbef1007206038251637caf31cccb2407ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1871 zcmaJ?X*3&%7EUX6wS**;AgN+{Y@yYmmWCjfXstzQ?P{q##UQ4!RL8C}Bo$jwHB?9K z8nFej47HS@mRdvATDxdp-rslLnIHFl=R4<~d%knez2AL)*V5z?-(@}k0C33^gSKX) zCp#>7xY#)l1`=Y!g)nOq13>kV)H<60eNb2w08o>`&vN5rbKYQ#V;BG+xAxz-K=9y{ z0sy#{P0=XZ$OoHblSg0|vEDoGZ)+r@g+tmUyU<*-;RN%=b}H3h2Lzu28(_X{s%*jfy|l{;(^YxYyckmEF`w5hXgfTXqJW?JhkF-?@7)=R7*&qSa`H>2Iwr48@W?V8Q27A+*Llc^Q9%J^7$ z&ufr~+*==0n9ZpqroU6O;pxCM;Wj2@+yVLeF~`9ttdrU*$68}Sn09Gf4a?qCbCA!V zTu@ra0TzVA)r&zO-{DEzw9lKL8uyNOJt`_I$D%@wZJ!Ck)Y}-!7B7|P;@2N#3c$2& z=kJoVaeu~~pWdjg$Nh5fwwm}HbTG}woM2Vs4WR9t&J z;ao72%Brey==u(?)!JBj#tb#eCP(?cd}Gl3gvSdprye>F{3T+cBwsI98-o0Nes-kp z)(MO{*(>v2?ah#x9PuHIbD~l&aB#gAx3jaGej1XzfC6En55JOT+TtEUlpI{A6UzMF z=^#&!wpE$Kg}EaoCL5Azk2x21Rt9oEE?f~L#gP|#QGQqGqFn|HAky9=lIY0z!jOCPKfWqGDu(@UR%|@s!S%;MSE?G5mAb zYU)H`HmEDU69Gq>GFxyP;lG1aY)UQi(3~g%5RF1XXoV~$X#Z0bWlYYzt>V&RJDFlp zpyPP%FfFfyLa1 z<}$zSxCPuJ;hif)tG!~6-t_g+=GS^E44e-bh|~<4Q(EfH5UOXTSaw7qfPy*!GxHTT z3XW~~*6q9!5B3sNl&G(Dp+_+*O?7p)-h)IqyTv8k_8f@D!7X@v_=h8Bv0;X^?w>b% zXAJKW_2(Od3Ipb8y5ry5k!EIQo}U^X(+cgYCBHTK3`?gK5jr{AozMwx>50C}MZu}F z?;{4-t`Vz`p~djr5zp@%gVoi@)WrRB?)z1;rw=suP8GrRm<-it#{M{ZqMsJV4a;9o z0Hxc95YWF`nb*TD5&VQ$rcr`33Gf^L7W9R!y*;E-F$;)FW#5@^cCR^CT#Dkd-EsFN zNlivaJhx%uaN(tXK?j zul)z_;5{-LLjNWimuI8UiI^->lwen)5+rX1u0@ceXYQofH4Wc;`@mPt5WK{me~sO< zE3m}Bf)2k<`85!u_-!6AjG7+$d5tqy|IRBH4Gd|;$~fAi-9`l5KFK&-`W*rx@sZYyj}*PHuX>mrH`^$p=J|>HOqI-!IZ2+OKfMFY6|Qx@pnOlu!ZV?2TCY%?2$tz$swM-f57{Bf4J`ZbNzUKKHuxUk|<7Rl@$*v0ssJIBEi;K zvi&3?f#f9f{vkE6WC5AZXRQIee(k>{Muvtb;{kxWyL+}T%Syb$?*um{0HFWnzXHbj z%4!1ua$ktH_}^HU7V|t3$==%CY*UT(uFx?f@#GKaOqM;;B}L8QFl&OSHwk>|cSrwH zD+`KceS?}68)-h}$lu%YAM^KQXrfa@ecM~xmL8koJG=b%p8}T0dp6(AqqY`AZUyzx zjrILTEb%lknUzbuJUb*x#H#o@$P(~){QS#DYU{f3@$qlQ#t>vOIk&y3vb>zo#@>Cf zQP4c{ByjbsMU@d18X}zPzB}`(&;}FGr?IiIN}DCHfK!7NE|{B}-wBkFhO6-Kr7>>A z>TFM<>JdFFo8V*rhJp`c(@nhFUR1g9r$-yF=N|byRA?jrq0TpfKJtWkP)o~{t!xtW zz=CPNY<1)N6(-JkMK?r_(iZ(|ek=zPqF3`FEg_I9XYDMeW}zdXUDL4|nezW49ZvjT zCP_LlFz~e+t2^ybJ>%<0`Ma>c-`IKc4-E`8nVhF32kRQX7N1JCURzsZObQi@kjM)s zkwvE7tv3v`%&!fur94kQAi8gUJ&*S+=yTb*0>7>l%{P^?$?W2H5 zCZ{6YpAdLn%`nNO=>r94zHW)#X7epkIb>=1z4zt@3g9a5rp8+%%lI>|i_c=Bw^BR4gqvp&DJH5wMNLu*=eI3}saq_-O_L=ByN@nQFq@9tm@gLE@HUnR!*IeFxuzxT-JJ;k zP7<(iim-_?Z@l)=dH1t`obZ9OI^NE1ukvcXAP8H06bq=?fSTgs77l5cUmJT>d!pCA z33qanmnl~x zo|EdK=VBusYhzgy=Kf#@Wdh)q)c6NJ4PvidplWY>`M6?6pBm<7hLa-<0m+nA!T~t> z3;JwMCGpP4`x3|Ohb=9hw?J?hVntI*X~6>b0`ev%_ES5%#yhElL(aw6?bSI=*Hd&< z2cZPv%e-Q4jf|2eMjxUPi}KDPKHh@6@~#*o77Y>T%;yJ%F+k~%0H4zGPik52s{AZG zhP@NDW@2DqP?1w>8$bX^%R9uKV11hq`cMSmw*>v}RIRf3J}6hIsFuDA9`%F--A>mM zZa6^$f_{Gc%$Vo^rqYYju&#-IJ3K|Hv>8!$aEAN&^ChLm4s&vhh^0JSOkL2>F-;4` zL#CHP-S-=k?5`b&Dyu0h)MX0Eo(cY=wK{328>_P~Ewzf3N;^2fxMPfAskvpvw$8@L z^`%#iK#N#Z?~ldNG99GDgAPHLgoaRG>p|?TVES+^{#~`l0ZY)t)Agl~a0mp#%^=GG za;^COgXL6i%fFG6 zO;TNqO0kEg8&YqxvrjKN(*M`yTGIKw45J2cQs!T!Mghmo>a8MwT*0HA3?|PVJCW}5 zXfD&`=wm^|YN_Od*@u@QyaaIa;8mH>8FeR-6+w{V`s zmy&)iH@QZGD1!X`GH}+O@4F&~7s}_}o;1H>NMInNJL{=!;H80fe(O=!sZTt+*Oafi z62$IFh(61Ry7wm>4)^cRv*41Yr6(mR7r16WM5cTZR(mwgI~YVHR5r~?!fS>N0OJOO zu~9!MVT&UrFiZ+VN|#Zz?@M1fRsD#8le!I!5;-X?n6|F3gH$ThN2uvhlp`0J<@0xSor@8i8!r;whJ{`cNwpj6d{0U=VZ&X H_KEukC&Y7a diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png deleted file mode 100644 index 7ba489c2bb1b9dbdf7669a1f69c06abfc111e1a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 zcmV;r14aCaP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX15ZgrK~!i%?V8PN z6G0e$s>6*JGczcaN_MJv92H`iCZ3Mo#ha^_fNGt+ zm1*Y+f}DFim|pvdhc~ZdU_Av`>~%!Hk&B6TtGM+!ix0^pb_xYMLp%-(vG8O(hPRi` zqeQ)=+b>QE^}t30{>>cX>l*G2UPCyuirU%!W(ATM5l~s$Cj4~+k<|^nUs(7Lgj^Uv z@y;mB+amxQT7=+oL(%d$IrRu}ZNDkx>k9N&uVDpGGg*S}!-gsM%jfWa`-Dh(({UlH zeRLe9Y5)}-N_kgEpv=bc{XM(%-=H*>ci1&Y2~=x9okfdinUFSrM_Y(w99tm=%W~O} zPKev>hTqRtV-7*(_xASkql{9d5u&OpdU|@GX&NRbCU}L3#_RPWolawNauR_+fQyk0 zDTI&=5-~eFi*PuM`T2Qtc6JJF!7jF+NTpK9X0w=^n}g5ivvcG^3Lz%jECz!?#N%<^ za;YVY9!?r;6B!DHFg!fWdml9d8Ik6xNQ%>t6bhMlK{xw*(y@)hIsG?}X-P+oMVHn8g^FsF`2TSWjA_0%bgIcY|m)G=XlT1>i5JDBQ zy}gaa#YH5ONh~cbVR?BOxm-?IPoZ8xOX{()F_gEr`uc<}hp8-G*HI`Gc+j~_G6-pgEUK7lwaP1rf`+YD>`i1t8X;|( zv`mOa6DOOvEmchL|GE#GUnouVeOfr_w?>GnKw(3WQY=6z6~)93i`~+nQrJ1A@w~&X zIZCi+rjIF@mF7Jh#_^9Fo{f(papfYgw+|S)oht}xy=;{<{e1%uZVV!nT>~l=y8;Dr zq6_eLAo}G8-u=s8oix})eSc&G(f%`BjuUuT!(Ay?a4r~tZ*Lc-;!LOa!otV0js+&9 c=$H`T7Zs%k0Plyy0{{R307*qoM6N<$g0o_(#{d8T diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png deleted file mode 100644 index ff999f4bc3b1e829029189e50cc9104fe20b0280..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1184 zcmV;R1Yi4!P)X1^@s6$6kv|00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1T{%SK~!i%?V3$U zBu5m-|2@<5(KFiPY)n{V2#KHvuZkD(7&|H+JeyPSlEV__J(^$iQ)PFTd2P$q5YP^r&mUhKpvU5 z-huu}9LVOZ+Dj5t;Q4A1HJ?3zW>jOf+Q8{+H=z&T5oaT$K)!n$>Z2c_9cC&fh{yWt zEX)r>Hy|;$C88jogQV+L;})i18-*hK-GXH+LoU2%NN8$*XJ-eyySx0e z)06AJaAD6$jP><(ba!{7p`iiEWD>GWDVaEE#x5@}qqDOUO-)URL?Tv=7)W5lFi=%h zh4%J#w6(RNx3?EHH8s{Z+{1-}%;c;wj=H&6bd00 zi(!t1u3KAMXl`!C%E}5B78Vc;2CZ+og9`PO`VRCoH2TF(?XJT3SLp9>?V5BxYu2 zxHzI#BMmx*yHitBn3$Nr^z<~w$H&pr)8o2Ly{zMRTIP2CL7k&cP@?rbo;ZGg^KIxu zw;`oYt=cD{fhQaG%A>YvC7w5XO%@(|zy2JNFBktge!uYsvk^bn21Qv<3rsdB{CyT? zxC-+49jK3gcHi+kxe=PhR48=3NQ@VS2A&3o+NMH+i*_ZH;M(omKm3UKjjUCBNqhl#e)tZNjc=e8 z!&p^IIIWJt5NRW*z`J-KYGMysGJSR4sqX1^@s6$6kv|00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1fWSoK~!i%?V8<7 zV@DXqpOeIoe3--_Rol8OMNqf9ffj60{{S%;7NQ8PwbTo}C|rM`bb${@ZWM-I|Gta#9 z&diChyStk~9?|J^*xTE~?H_*vii)IE{#cMf2!iyBKSCmo51dX^R8-*PM7 zEG)z?zil9}FpCc_RU&?TEah-Qqk%;u;JuU{cXTm?(&FQ!(^4i61n3KNxb?t`+M6!C zW4Gg&_cNSA5kY>v_dAkbe}Ru*e+5xnnb@j0lQSHe6~#d02*y|hjuR8y+6W?rq8FO2 zq9`0@G4)dv*BE?+xm_PFUB8L?t5;tPK@y@MA6fMX-})9AgOSfH)@h;n{ySKIxDAKS z26sAy!?)f=s&4>DaS%kSh2Z}i=7Ao}`@^`bfFR-&Qy6hZBYX&fgCQ1&kW5GwOQgiX zN*?Dd|Exy+Wc^hTv{Z&?0 z!sqjG6{W1K3@t4!uvje2CMPFZnp{*T$i~J7W@l&FK=oLc^Uov{g|7ZLsL@|B9Vwx;`vD=5^y@5SY2Jk*w`4GSVSNYkR7vBB}kTh+!fJM zNHf=JwIUP>u{0V$@)*kFK`NC(U0oggem`p?`y6d;ZLBRhs8W!8K?6@-NZyxBCfV{x z@#^qUJOFZnL?I}kPfbl>X=#bIC0FzdB}f(uwzRbD?Cdaccy1gBs()r?23=iU=;-K> zQsr^s1fiKr>-*^FC|km|wzlAQyVjcUsbK+~^5mc@L8#;F z>+7)F?HC#w!ph1D_V@R>h(dKA92{VKdmAp73&X?1?2~p}G;llwDK0K%+uZK%ZuIo@ zFzf8>L~Cm+;_ggzd{r>rv$Q0`#JmLDC4r%mY1bGSVPACxPg6h%US%&53yZ>Fk zUwa#=PX-|5xTPgga-BKC;jr}W|77U;{n101UDst_ztdUdY)pzqqq48;RkvhO$7z7b zVzJm0#W)YN)5+F-+IT|e8rt%nwr6>NqRJR@kx^a{3T~oHXE+$E=WKkT6Y-gH=QXN_ z^;bcZ6-29p;PXQ8didC%dyn7jWku15VkF`aJP&f0Kac(8WBpbN#e%x#n+3do`BjRb zeE$_fmv&+sttkWU>Y}t0fEawFOb(ETm`d=~eGkg}Z@d_Pa=0fRlomdl#A{XyI7;y^ zG=Kld2zW!t;PJ%;U&#MFfgo;7e2QytUdf{%z(4gTls?Llpt}G7002ovPDHLkV1m1j BSQ`KU diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png deleted file mode 100644 index 6d330baa6e76585ea860dd7d758c51d841081675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1101 zcmV-T1hV^yP)X1^@s6$6kv|00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1L8?UK~!i%?V3+W zR6!iazi-!7@xQBLB1)x%fr#iq58_$TQ#T2SxDa zB|U@%8fBa5f?Z6*)pd7uxA_iF=nv(-nzydYeqeYo^O*PM_nrC8Z+`D-8yg!zm`HMR zG9C>N49U2=OaddQ~*P4Urf~2LT;a1lj zyi5=S(JUOD-l70a$dQ#S zNXy+GJhi&vO-V=KdZ`*wD26C$uV2CO-i4--$GDt#3CG9B`VDG`i;KhB+8U;(r_tHj z8AXEN8@$EW;lK9~@cQ)D|A2y6EwDG=z&+R3s1Zc?kT4PuKHh%}?eIWJ5!TT=jvm4* zNQ^Uy2@+xavmlH<%*5>e&BS7{z-nbMZX82`&`3#1NyyF3mGQV4MoUOYKxSqpPQ;nw zB8rlgl_jscySvg(MhRIE8iz6d=;)|?XkT9+Qd3j)RyD-O$76ne9<{Z#XliOgMn;C- zaxtI)_4V~AEG$G>Ss5xTE0LX@t+$3o$bxVQI-O3qTrL^<+3j|{MGdF@bLA~AE*c)l zd6Os(s6#_TaagzXYzz$z4VapmlJ(qAj9}<)ibBJg1^ZTgunQM~KmY{=1#<83cs$tK+mrQe zZEc3~U&iE%@mDAcNl#D5z`y_o2M49QGBSx{f+&Up%+AiDsHh0t-Q9+hoe?LT2xl>`1$XO2~rHSSu?l*xufj-vt=vEi5b;df4CKfHpTb zv9z=#{V!wqnVA{bY&QLl$cB@1e0&`3?d|C4>5-+QqXVt2t%fqhhSKj8h0Bsx`bI_% zXGKLnrQfe!gTMCyH21#V`ZE-T1Ea|7pP}UGiu?^8RK!#gyRSm zLoSQa0$*rNgnbu>SDE;n_v z{yJg8Flv5sxZB$cpK~1+x*5e-g(hD-US#Cw!=^`nDxo0UoU5v;(9+UkvLL{BC-(_P T3zUm!00000NkvXXu0mjf*z*N@ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png deleted file mode 100644 index 76947671dcfb96d6293f4a081d5f40e39ce2659e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1165 zcmV;81akX{P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1R_a9K~#8N?VL+U z8$lSy|9P**7?mpOMbsk2Xsxw)5KrPoL3;4s)*b{Aky5c25w*4l1&f}%c@sepO9j!p z52%V23Pp;ER-21h5%bVQ)BVO(N>8oXjgy`I!G~lw>@xdvX1;lBOg^74h9o94GZPIB z4Jauo!NtXea#TGm77M&yF9rq%P+VM$Xf&#vQI}XO28Y9eq!*H&o{pB57R=7hvO=ki z$z;OF$Oy{H%HZ*MG#6r4J|{I)GpInv$H%J2`T04*;jnTi?a_r0Jz)sp3_}QK7(zHh zI3bkArlzLCZnrB(_(CWlX=!N)g+iE^n84Q778}FxhA=`>Qc^H8GlSgRT(q^dp{Aw= z!^6W&5>E&tgvJxo)6;Bhv9Ym%tgI|_cXz{Pvnk);17U>F;Bj(tl8wA8D=X31*vO>t zhERP-AP~5j?^<47Mqghat2sO&ln{dEi&j@xk&}~yj*bolgF)pC9|$FcQeH`tFgiMl zm6a8CjD%1##tXs-F`LbBxm>8Ou0~y59mdASShhvAg{O{P7$Hul6ZQ4=SYKa9Yilby zJ3BExK8~iQCPX3;xeE) zfUsO!;>BWx$^QfHo>v(1|4Nt;xlZZ`Y7);QFol9~2Pn=|5c{9#ONb6Igm4CzLMWA` z{zpW(5<;CC?UmZz-e%($5v6b=gk;Rm&!e`s7FAVMI6OQwgxn=(az@kK+{|8zv0AMx zM1-p*i*j)ak>Rpa7n!((BzkIE1a2*aWE>qG8B$b+s~SSryUwhrQx{S8t%XqMOJfGL zI$?VF^tS8T+8Ud{TUuJe<>jSvRvhB0A!=qswCcCHxrwQ%DK+ zcvV_jitOxcxZQ3P6cn(RYecIhT>blzlt060vJCDVeF>oq&2B?1)fsnyLXwmVCk`F0 zsYh{=o`Ki{59BmWIU5I*a)(23ef@$V?@#@#sqgOYVqsx{{k)MD^Hh{BlaNnN`9V>a z6IF4)u1Rh{BnL*mdEx0DL|L-`JQE3_(LB`-dM#NiAr_kr7xG`N3K9+k@TRK^`FVLP f+0cS3ArAZoK3I)vU=gW)00000NkvXXu0mjf*gpo1 diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png deleted file mode 100644 index 8862702f182093687b4fecb187ff755e7b4569cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1357 zcmV-T1+w~yP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1ma0VK~#8N?VMjo zWK|r;zw`f}IZ9ImYb)-9koX6E=_Qy|0(FaAbrK;rQ(r7Z>`Cw?@=ftYAv75Wq17;Q z@kt0rR?u7rH56Q56$PoRHOGH5Iy&9o>FyHdi{3ML4t73pnBkto@8bD*{{8M{44a#q zap-`>V!_(l8vK4g91aKKalW@T8jUQb!C=6}#f5s64-_*RjiREW0>#C}?0cfnO^DTM z#njXkoK7cvJ|A}>)M?e#)fgWiPe|-gD1@1r88kFB;N;{)t>KTZLZ+vuS!a=uNF<`x z@WJhNzY?;)zpq~91C8#*#YHSFEup@?KA}R4>Ze2yq!7W7LIgt!5e#94m`owB!m=_l9Gahg98i<3}AVA8R_ZiY7HMK-rU?AhK7dNnS$hqf^b6Yb~`2~ zCy|knfws0bc)ecq_V&W%a;Yt}K{6<2X=y21TU(Kpm4)5iU6{>gwMB9fP6%}joh9hB zt(=kI@p#bQ-p*y}q8Jprv9XbzhdMhu5sSsp-QCTmFA)$<$l>83n@YmrFfubUQBqQZ z?Cfl{RvV|Mrzk5cLrqN$i{H`F!KSRdygVjG1cY2eNXpB6R4^DsS63Ie(=kb*ECJ=d zR##Wqh-_|dPUz|TItVF*pebZ*Yz&o^l_)PSXC28C+Bmedw6JstjS}AZubU#I5Xv3V zC|OxqVcB8otnKY>_Fv5dTG#`D0Q&m+P*6~St*tF#w!gv&p^l+xq_D7%ZG9=3mz$f* zWKh;sE2*ien3$MAPfri}`}SBIviCU#u}gcCxeqo}9|J3BiV92~^x=qT3L*O8Nx zgY)xqwbjj_*px=6X>em>1H;3^7#SHsety2@*t`%<2ralIfOb8!Zk(N+u_Yn9ej0A zC>^M!u+I8Vhtc(`+h4cfLYE2T70DAKxe_TvFr*N{kU|7Q3K0w`L@=Zf!H_}(LkbZL zDMT=&5W$c_1VaiD3@Jn~q!7VKo)DWAafefL0Tt_|H$qHi<(lQn_SY>i8kB%k^dUKq zaNkJ7@%s;e*cp44qzQWR`1`MLKVQYeixf=Qq6nwGj(C0{y*H{h(tynd`_rEg`g0!( zB_G_bXX;b)^LTCeTU6eC3(8-m)^LENSj?!3*zv@rNQf$hWb?vq$MPS02zno&;kLYW z^3TsRJ%>Lqq|7qPbf8}61Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1YSu*K~!i%<(XMX zb4M74-;^s!%K_S`$}3%7!}ATCs-*q=ht3l+Q*L_tM_ z7E}a5yb-){xlkGl+M*T}QCzc{Ki_bK|Dri*lT#e&14EK?P7dG9JM%3w!pX^r8!`|Y z8j8loMkFOA!R2y^SN%t&Qel5@A75sF1*aqM_bG#v`0eNrpM7p-?{>Qb>$XykGIA)6q2y&eMt1DKeYkj1#5pdbuh~lnK?oO|uzQ4|72;2$wcvgia=a5k{!jQ6M61=Z42m)n z9--h$@k^)EK@bG-DnC#*M)8pB`zJ^WVltU9H8q8{wl;Mo}>glJv|s3 z8$(D)2v?%0)oR?`-J!d?8zUnlh>VOB?|8?nAharKYHBb#I?B?!oSYn7UtcrDdEm7U z*xK4cVqzjXIy%tQ)Wl{Z4=5B0JU>4pJv|+(tE(6u9!6DF6(S-c#3%k9uYx>1J)ya| z8E0o_*xTD<*LHVznTy~7sv{B-5>QuH$Lus3jVLcKhuv-$pLknnr5+p{uoB?(^b`dJ z1z2BS_v!k*3PO2xMn(n`d`f4GnD7NFff>1|~j*d`TT8jSueir0utAH{Y9w0G_i;L0K)x~~qZ*QZsvl9-7gZ<`# zsHi9u78YV*VF9tRv51b2MoLPGq?hq32nFwiSlY2rRlsumJbu?DgVXW&0{g8S=CD`@p6`L$ nNCn@;#RbdsVq#)s5(M}N%M|rR4(q!;00000NkvXXu0mjfNy#DB diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png deleted file mode 100644 index 216a0f3a1209d7b126eb41f358c417c5a999e974..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1352 zcmV-O1-JT%P)%z00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1l>tQK~#8N?VC$T zV^b5WfuHR}B06 z`#yvTMx#;rB#*nhJLQuc!bT8^aejU-hAb==3v4!3iqzB%jFW|`1n}gLv(aOf(~7fAVaDkjCO-{ zL_|c0eS+>SH*2+n=Xs2@inNL$|7ljvPEJmg8TByXXY`ttZ|bmGEqK}6iRX2n!{hNN zvw>qan=v~(E7C1NS;A$8w56p5wY9ZUQVwRb1>cQ+kCW#=!sm^^o&Oam2LCI{Y{c~s zS#W&u3U1G-awKq=^O~QZM}L2R@CxE|Ix#*zE~T@Bi9N?x9r*L*H%Rj2!e)HmFUh;R zp%jB+{$YBJH_ih5{Oz)|>wAovx^zKCMnk43;;=*^VoGJLx z-QFNWb3y2x(EidZlLcMS{|lN4LZ6+U$>ijus8=&jq*fHh7V+`%I5;>E@BAL>XeJ1s zVhWLzlq7uj^z?MZ#lR@&90usABV$%ii!%<)zyh2 z5Yya&!cyzZ%nXi>jlo_OG`s#Wu>TJtJ#q6+jO?Kw~?KljndLmDUH-miDrVZ zt%i+f$;rtW92^vLjF{CX=op|eI9p#|7lMa6ng~KM78e(hk&z)b-JP8sq^71KCnrbM zZ#C#y(exD*6o`}!_b6rzLK#g2pM8~X1|*hnfxiF}w06d%Zp)tl0000< KMNUMnLSTa2)q1Z0 diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png deleted file mode 100644 index 1e0555648434f497b65bd92d8c00607ebad0310c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1335 zcmV-71<3k|P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1k6c9K~#8N?VC$T zYeyKz|9L0A#Va*Fu0qADU|lIvY=sIIH-gYrgEruEAzEK>s~~7a5w*Q8TvTJ#ZhR~S zi@0znD0pobg1risplL1F;`FMe4 z`~A`#c^C`^bai#1rKJV8x3^NOID`bzOW%b;uh--H`WhD(7u>G<`+L@}=!ieU{~2LH z1S2enV5n9Qolb|y$VkM;$4f2RK$U`!7!>r!$H$S7kbtA3BN&ZF>8`p_tssw&k2p9u zz{$x8+plN^RSH64q@<)EF)>kU(GIGOA+%xSwTl-C)e53j!h#5fY6YR(R4C6y4vPZ4 z7Eq-il#|-s-G#|yLT_&`R#sM6zDf(Cs8SGeS;@)CXl`ysXJ;oWD=QHZ5uu47ui7Px zM7g=S!PV6jt8ZwhbI>Vw zzsC+d@PMBA{QMl#)6=p=;UY*>R1}(;nlL*%%WBpVH&Yus^od1jpv z`e+}Xec6o>*VkMRPE=P{V|aL&xji{>6Qs4ZmCJuf2wJknU(LY%$q(=*dEl_Vf;aH= zzD+tSe!l+^ug!CK_SH*#Gug*&tT8b$sI9HV$jAscL4*zJj|i}r`H}TAd}whz`TE%M zInsPI^G5sDlPrmNp3ySd|^(a1J+bbCViXfDYrFg^JA`@DvrH4>l z5DL=!`}^qW>0xdxHa1qeBM)*obmD~S>hzcFvKCNO5ONK4aC&WR4adjF?7VJcVF@8y-rgQo zS6A8L>9)2uCXCuqe93xvcwjquN?BxOWud*jUH0j7I+I1|6}#Pzl9Cc+XJ>N}S_Bjq zgq9WBG#wru!sGEEFE5Wv93fFiY+`3;2i@J>tWE7GE(kfRxVShhEG)1Yvc0`6-H{Ik z^M-~7_TwHh4^*^LJBkWIXK$(IeRp@qzGq})ptQ7^aR8+*4#esnVHcQlwqJo^A zoiV4iyu8c=p%_BeRBrGk+q<>3g~`cDmi~9STr9!Iv(E8fp{O8~!K8XYK|uj>a&mBb zddkHKkxL^hO{Yy94hO2Ls!&{9{BCJfGl~aw5`|_7ucA;05`|_&=+c51gyMpP`DX|% zhyf-Tf+LL6$3OY9>5Z`JqNN_iM|Q=KcHT2HGhEA}R5ARu^n3hi{|UD~`GML38w}BY z#J9Y}@C4ryE}50V!9g)fxU}P@nlkM#cs(S!Fpv9pcoY7B&m4Tn0G~-5F#2HFeU2Bo t@4wsO%Y`z`>+9>xMUr8W!{36K@fWLmVp~iu)J^~Z002ovPDHLkV1hW`YS#b& diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png deleted file mode 100644 index d3dd36bbc10fb66ad83d8c04d0214e5f25836739..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1523 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX1&B#RK~!i%?O9t$ z9A^~%X7=jtsw?KfOSQ2sw6sW-;$@+TB#jBQxhNQNK?_FF7p=yI*a|{JNFt<7=u<+p z#TP|V1SzEvB~Q_UNYXZ5ng>(G7|kxm8ZUcucRJ_qXp<(c>p#u zW-skTV*MmM%RWd5DZ6|yo6VT*7txg61;=}DVr`AzM@f=kG#a5DAzmHITAg_7a~BGw zQ}9a5%I@}I5-hl-Ys3As3CKMeixu%PGWDMz0Izbz_Mn3v?%)*0Oh4h_>GzS?@+$&q3KDY7iG=!TblD_$L;UJ9iy}Vrq~VWq(fFXw zhIm5?8lt;olKE^JA!6A7GrpBR#{3sf*v}2(lJNoJ#bg9I2{8*I8dl%MNVf}LrvIoJ zA)<1uEo6#4(jxZ?$}xJnoCM|E@T;=vm4CmgSo;S)IfFgY(toatd6A0-VK5lj(i);%I<gte?kbpaP?x<2W`E=;eA!KG|!s&G4*s)`({3J5GUN2f( zTbaKUz(`cR8E7oNa^(v4?%m7m*3{IXx3`zs-VB=(LV>z{`!*^oD_P*wsyPq{ASWjW zEiEk=8X98fmY0`V5O_e!y?ggA%FD~ygi>2ui_XqYEG#T=tD5XstyUa8dX)KVv)Pm# zJ`f32RaMB&&PG#H6N@PcC)_l)spsSnWkyz3mU2cN=+6?VOG`_rudhc*NeTAs*#p1d zuUxS`gygY0H8mBxckhPJ=RFH_QyLS&gJw0e_Y(#W)w6eQ>P~a(e)SIqey^8McZmg`VD7&u) zNGwG~MU0T~@o`jCR3IiMM%jHHe7VgDDhFw4X{==F^?D2s53^4-AVR4wS65dfFE3A1 zb*lwUlGH0{fFS`!f|o`JskpSXlw}=(K93wZ!W6^{DuLAJE?l^P+1XiE3j6x{Hl{fq zP!_Kb8Wd38enh{WpPy%qoqQY{8)N6soH-LVi^JZH)cfSg6ZT#0a|8W;U|;~Tv9W9_ zqDhO=cXjpIC+J8)fts6}!~XsIF*-Vm=H_NLUKSP>ZjjCcYQ#5g+}M~fXJ%%&NTE`n zl$34FSwglJ7Z+9ABO7kFo7sybbHnB$ z5kM@{(yCd88~I*i3qrUdKkM0qIPRsBy%O+mdDO!QqY9f=(G0Wj?09VvV&%Q)=dO)( zk&oh2K57L`--mqqeI6W*kMXvD7XlLh^rRPzm^A%@Kfg+W^OPNK;Xd9FGZ2*cnzbN^ zSP3n_Wf{YRlNm_<`ZuI{bLIOOxHFdds2{@Q55Vb^SMUWD)$P5FyT&Q|l09JsV6qEZ&Sq?5=qPd~1;C#f=KkcAH6X-@ ZzX85K1*M9_3l;zX002ovPDHLkV1gX4y-5H7 diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png deleted file mode 100644 index 40d6ff65f97e0110b9e2b68ef87e32cf6b16e0a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 934 zcmV;X16lluP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG&=l}pD=mCOb1snhX13O7XK~!i%?U_wT z8$lGu|GP;xk{Bh2Ab1m65L*x;_QO*>6>Rm8g9z zW}AjE#+17RBfDAsh~4 zdU_hEREoFCL8>6epBx6hKgGiV4X@QX7=<(5TOA&?9-j)Y@caFJEYJN!;w-^?#2`UV zPEHVsL@+!&YN7FR;{eD(tjCa>4 zD+n!GE|MA-qI?&bCg`=Y*Ta+u@CFFtpCg$honKPp{ z5on*1NFT7-QC6P>?}MU5AU@h#9w&`HjB;8O?JP&zRuoo5h$^@w6uiQ z)>e#;jxy_rMx(Y5xk6b%D4W^W*N5ZdV{~_SBb`q3p6Z~S&-nN_c6N5q)6;{)!$W2v zB)H8bWd)&)9EGN|aFkSYxm>)bdMIn#*w~0*Fvvn+S;4|xq9Pwck(ODkLYqw6Dz2v@ zL8?WC6OPiz^@?Aps0xj7D^vUDQTF!sq@-0Td}Ze$(I@PHDyLJ%R?MQ5k{FKKJYo|q zSpN7+*l(H8+{^bd#bPmOf@F*wCO(7_c%Y#m7K~k>I*Ukc z)#6$EbEGrYj!7wPL;mXE-~i3d&6Yoz