h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

Main.qml (7920B)


      1 import QtQuick
      2 import QtQuick.Controls
      3 import QtQuick.Layouts
      4 
      5 Window {
      6   id: mainWindow
      7 
      8   width: 800
      9   height: 600
     10   visible: true
     11   title: "Nissy 3.0 - Preview"
     12 
     13   SplitView {
     14     id: splitView
     15     anchors.fill: parent
     16     orientation: Qt.Vertical
     17 
     18     handle: Rectangle {
     19       implicitHeight: 3
     20       color: SplitHandle.hovered ? "black" : "#AAAAAA"
     21     }
     22 
     23     component MyScrollBar: ScrollBar {
     24       orientation: Qt.Vertical
     25       size: parent.height
     26       policy: ScrollBar.AlwaysOn
     27       anchors.right: parent.right
     28       anchors.top: parent.top
     29       anchors.bottom: parent.bottom
     30       contentItem: Rectangle {
     31         implicitWidth: 4
     32         radius: implicitWidth/2
     33         color: "black"
     34       }
     35       background: Rectangle {
     36         implicitWidth: 4
     37         radius: implicitWidth/2
     38         color: "#AAAAAA"
     39       }
     40     }
     41 
     42     ColumnLayout {
     43       id: mainArea
     44 
     45       property alias scramble: scrambleRow.scramble
     46       property alias solver: solverCfg.solver
     47       property alias minmoves: solverCfg.minmoves
     48       property alias maxmoves: solverCfg.maxmoves
     49       property alias maxsolutions: solverCfg.maxsolutions
     50       property alias optimal: solverCfg.optimal
     51       property alias sols: sols.text
     52       property alias solsHeader: solsHeader.text
     53 
     54       property bool solutionsLoading: false
     55 
     56       anchors.top: parent.top
     57       anchors.left: parent.left
     58       anchors.right: parent.right
     59       anchors.bottom: logView.top
     60       anchors.margins: 6
     61       spacing: 10
     62 
     63       SplitView.minimumHeight: 180
     64       SplitView.preferredHeight: 500
     65 
     66       component Separator: Rectangle {
     67         height: 1
     68         Layout.fillWidth: true
     69         color: "black"
     70       }
     71 
     72       component OptionalValue: RowLayout {
     73         property alias currentValue: valueRect.value
     74         property alias from: spinBox.from
     75         property alias to: spinBox.to
     76         property alias defaultValue: spinBox.value
     77         property alias defaultEnabled: sw.checked
     78         property alias label: sw.text
     79         property int defaultSavedValue: 1
     80         property int savedValue: defaultSavedValue
     81 
     82         Switch {
     83           id: sw
     84 
     85           checked: true
     86 
     87           onToggled: () => {
     88             if (checked) {
     89               currentValue = savedValue
     90             } else {
     91               savedValue = currentValue
     92               currentValue = spinBox.to
     93             }
     94           }
     95         }
     96 
     97         Rectangle {
     98           id: valueRect
     99 
    100           property alias enabled: sw.checked
    101           property alias value: spinBox.value
    102 
    103           width: 65
    104           height: 20
    105 
    106           SpinBox {
    107             id: spinBox
    108 
    109             width: parent.width
    110             editable: true
    111             enabled: parent.enabled
    112           }
    113         }
    114       }
    115 
    116       ColumnLayout {
    117         id: scrambleRow
    118 
    119         property alias scramble: scrambleRowLayout.scramble
    120 
    121         RowLayout {
    122           id: scrambleRowLayout
    123 
    124           property alias scramble: scrambleEditor.text
    125 
    126           spacing: 6
    127 
    128           TextField {
    129             id: scrambleEditor
    130 
    131             placeholderText: "Enter scramble here"
    132             Layout.fillWidth: true
    133             padding: 4
    134 
    135             readonly property bool empty: text.trim().length == 0
    136             readonly property bool valid: NissyAdapter.isValidScramble(text)
    137 
    138             onAccepted: if (!empty && valid) submitScramble()
    139           }
    140 
    141           Button {
    142             id: solveButton
    143 
    144             enabled: !scrambleEditor.empty && scrambleEditor.valid &&
    145               !mainArea.solutionsLoading
    146             text: "Solve!"
    147 
    148             onPressed: submitScramble()
    149           }
    150         }
    151 
    152         Label {
    153           id: invalidScrambleWarning
    154           text: scrambleEditor.empty || scrambleEditor.valid ?
    155             "" : "Invalid Scramble"
    156         }
    157       }
    158 
    159       Separator {}
    160 
    161       ColumnLayout {
    162         id: solverCfg
    163 
    164         property alias minmoves: minMaxRow.min
    165         property alias maxmoves: minMaxRow.max
    166         property alias maxsolutions: maxSols.currentValue
    167         property alias optimal: optimal.currentValue
    168         property alias solver: solverRow.solver
    169 
    170         RowLayout {
    171           id: solverRow
    172 
    173           property alias solver: comboBox.currentValue
    174 
    175           Label { text: "Solver" }
    176           ComboBox {
    177             id: comboBox
    178 
    179             currentIndex: 3
    180             textRole: "text"
    181             valueRole: "name"
    182             implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
    183 
    184             model: ListModel {
    185               ListElement { text: "h48 h=0, k=4 (59 Mb)"; name: "h48h0k4" }
    186               ListElement { text: "h48 h=1, k=2 (115 Mb)"; name: "h48h1k2" }
    187               ListElement { text: "h48 h=2, k=2 (171 Mb)"; name: "h48h2k2" }
    188               ListElement { text: "h48 h=3, k=2 (283 Mb)"; name: "h48h3k2" }
    189               ListElement { text: "h48 h=7, k=2 (3.6 Gb)"; name: "h48h7k2" }
    190             }
    191           }
    192         }
    193 
    194         RowLayout {
    195           id: minMaxRow
    196 
    197           property alias min: slider.min
    198           property alias max: slider.max
    199 
    200           Rectangle {
    201             width: 100
    202             height: 20
    203             Label { text: "Min moves: " + slider.min }
    204           }
    205           RangeSlider {
    206             id: slider
    207             from: 0
    208             to: 20
    209             first.value: from
    210             second.value: to
    211             stepSize: 1
    212             snapMode: RangeSlider.SnapAlways
    213 
    214             readonly property int min: Math.round(first.value)
    215             readonly property int max: Math.round(second.value)
    216           }
    217           Rectangle {
    218             width: 100
    219             height: 20
    220             Label { text: "Max moves: " + slider.max }
    221           }
    222         }
    223 
    224         OptionalValue {
    225           id: optimal
    226 
    227           label: "Above optimal by at most"
    228           from: 0
    229           to: 20
    230           defaultValue: 20
    231           defaultEnabled: false
    232           defaultSavedValue: 0
    233         }
    234 
    235         OptionalValue {
    236           id: maxSols
    237 
    238           label: "Limit number of solutions to"
    239           from: 1
    240           to: 999
    241           defaultValue: 1
    242           defaultEnabled: true
    243           defaultSavedValue: 1
    244         }
    245       }
    246 
    247       Separator {}
    248 
    249       StackLayout {
    250         Layout.maximumHeight: 30
    251         currentIndex: mainArea.solutionsLoading ? 0 : 1
    252 
    253         BusyIndicator { running: mainArea.solutionsLoading }
    254         Label { id: solsHeader }
    255       }
    256 
    257       ScrollView {
    258         Layout.fillHeight: true
    259         Layout.fillWidth: true
    260         Layout.bottomMargin: 10
    261         ScrollBar.vertical: MyScrollBar {}
    262         
    263         TextEdit {
    264           id: sols
    265           readOnly: true
    266           font.family: "Monospace"
    267         }
    268       }
    269     }
    270 
    271     ScrollView {
    272       id: logView
    273 
    274       property alias text: logText.text
    275 
    276       anchors.left: parent.left
    277       anchors.right: parent.right
    278       anchors.bottom: parent.bottom
    279       anchors.margins: 6
    280 
    281       SplitView.preferredHeight: 300
    282 
    283       background: Rectangle {
    284         color: "#404040"
    285         radius: 4
    286       }
    287 
    288       ScrollBar.vertical: MyScrollBar {
    289         id: scrollBar
    290         position: 1.0 - size
    291       }
    292       Label {
    293         id: logText
    294 
    295         font.family: "Monospace"
    296         color: "white"
    297       }
    298     }
    299   }
    300 
    301   function submitScramble() {
    302     mainArea.solutionsLoading = true;
    303     mainArea.solsHeader = ""
    304     mainArea.sols = ""
    305     logView.text = ""
    306     NissyAdapter.requestSolve(
    307         mainArea.scramble,
    308         mainArea.solver,
    309         mainArea.minmoves,
    310         mainArea.maxmoves,
    311         mainArea.maxsolutions,
    312         mainArea.optimal
    313     )
    314   }
    315 
    316   Connections {
    317     target: NissyAdapter
    318     function onSolutionsReady(header, sols) {
    319       mainArea.solutionsLoading = false
    320       mainArea.solsHeader = header
    321       mainArea.sols = sols
    322     }
    323     function onSolverError(msg) {
    324       mainArea.solutionsLoading = false
    325       mainArea.solsHeader = msg
    326       mainArea.sols = ""
    327     }
    328     function onAppendLog(msg) {
    329       logView.text += msg
    330     }
    331   }
    332 }