300行代码不到的javafx框架tornadofx拼图游戏

2019-09-05 18:18:25 浏览数 (1)

不知道为什么分成9份的时候无法移动,请高手指教

分成36份的时候程序有可能卡住没反应

分成4份的时候有可能无法成功恢复原图

heart2circle.zip

附件运行方式:解压后,双击文件:run.bat

代码语言:txt复制
import javafx.application.Application
import javafx.application.Platform
import javafx.beans.property.SimpleObjectProperty
import javafx.geometry.Pos
import javafx.geometry.Rectangle2D
import javafx.scene.control.RadioButton
import javafx.scene.image.Image
import javafx.scene.image.ImageView
import javafx.scene.input.MouseEvent
import javafx.scene.layout.GridPane
import javafx.scene.layout.VBox
import javafx.stage.FileChooser
import tornadofx.*
import java.io.File
import java.util.*
import kotlin.math.sqrt

fun main(args: Array<String>) = Application.launch(PingTuApp::class.java, *args)

class PingTuApp : App(PingTuView::class)

class PingTuView : View("拼图") {
    var N = intProperty(4)
    var swapN = intProperty()
    var timeUsed = longProperty(0)
    var n = random(N.value - 1)              //自定义的函数,产生逆序数为偶数的不重复数组
    var m = findnum(n)  //找出那个不在随机数组里面的数字
    var imageViews = (1..N.value).map { ImageView() }.toTypedArray()
    // 大图片路径
    val imgPath = stringProperty("pingtu/1.png")
    // 空图片路径
    val imgBlankPath = "pingtu/2.png"
    lateinit var rbox: VBox
    lateinit var gridPane: GridPane
    lateinit var bigImageView: ImageView
    lateinit var smallImageView: ImageView
    val bigImage = SimpleObjectProperty<Image>(Image(imgPath.value))
    val smallImage = SimpleObjectProperty<Image>()
    // 大图片宽度
    val imageSize = 400.0
    // 每个小方格宽度
    var smallSize = imageSize / sqrt(N.value.toDouble())
    var start = 0L
    var timer = Timer()

    override val root = borderpane {
        paddingAll = 10
        prefHeight = 700.0
        prefWidth = 1000.0
        primaryStage.isResizable = false
        top = hbox(10) {
            alignment = Pos.CENTER
            button("选择图片") {
                action {
                    val imgType = listOf("*.jpg", "*.png", "*.bmp", "*.gif")
                    val efset = arrayOf(FileChooser.ExtensionFilter("$imgType", imgType))
                    val imgFile = chooseFile("选择图片", efset, FileChooserMode.Single) {
                        // p初始目录为当前项目目录
                        initialDirectory = File(File("").canonicalPath)
                    }
                    if (imgFile.isNotEmpty()) {
                        var imgpath1 = imgFile.first().toString().replace("\", "/")
                        // linux系统下文件绝对路径以“/”开头,windows系统下文件绝对路径包含":"
                        if (imgpath1.startsWith("/").or(imgpath1.contains(":"))) {
                            imgPath.value = "file:$imgpath1"
                        }
                        reset()
                    }
                }
            }
            togglegroup {
                listOf(4, 9, 16, 25, 36).map { v ->
                    radiobutton(v.toString(), this, v) { if (v === 4) isSelected = true }
                }
                selectedToggleProperty().addListener { _, _, _ ->
                    N.value = (selectedToggle as RadioButton).text.toInt()
                    smallSize = imageSize / sqrt(N.value.toDouble())
                    reset()
                }
            }
            button("重置").action {
                reset()
            }
            label(swapN.stringBinding { "交换次数:$it" })
            label(timeUsed.stringBinding { "耗时:$it 秒" })
        }
        center = gridpane {
            alignment = Pos.CENTER
            gridPane = this
            isGridLinesVisible = true
        }
        right = vbox(20) {
            alignment = Pos.CENTER
            rbox = this
            run {
                //显示空格子的图片
                imageview(smallImage) { smallImageView = this }
                //完整的大图
                imageview(bigImage) {
                    bigImageView = this
                    fitHeight = imageSize
                    fitWidth = imageSize
                }
            }
        }
        primaryStage.setOnCloseRequest { timer.cancel()  }
    }

    fun reset() {
        start = System.currentTimeMillis()
        timeUsed.value=0L
        swapN.value = 0
        bigImage.value = Image(imgPath.value)
        run {
            imageViews = initImageViews(N.value, imgPath.value)
        }
        initView(N.value - 1, smallSize)
    }

    private val swapListener: (MouseEvent) -> Unit = { arg0 ->
        val img = arg0.source as ImageView
        val sx = img.layoutX
        val sy = img.layoutY
        val dispx = sx - imageViews[m].layoutX
        val dispy = sy - imageViews[m].layoutY

//        println("m:$m, sx:$sx, sy:$sy, dispx:$dispx, dispy:$dispy")
        if (dispx == -smallSize && dispy == 0.0) {               //点击的空格左边的格子
            swapimg(img, imageViews[m])             //交换imageView
            swapN.value  = 1
            if (issucc(imageViews)) {                          //判断是否拼成功
                timer.cancel()
                warning("Info", "成功!")
            }
        } else if (dispx == 0.0 && dispy == -smallSize) {        //上面的格子
            swapimg(img, imageViews[m])
            swapN.value  = 1
            if (issucc(imageViews)) {
                timer.cancel()
                warning("Info", "成功!")
            }
        } else if (dispx == smallSize && dispy == 0.0) {               //右边的格子
            swapimg(img, imageViews[m])
            swapN.value  = 1
            if (issucc(imageViews)) {
                timer.cancel()
                warning("Info", "成功!")
            }
        } else if (dispx == 0.0 && dispy == smallSize) {                //下面的格子
            swapimg(img, imageViews[m])
            swapN.value  = 1
            if (issucc(imageViews)) {
                timer.cancel()
                warning("Info", "成功!")
            }
        }
    }

    fun swapimg(i1: ImageView, i2: ImageView) {              //交换两个imageView的实现
        val row1 = GridPane.getRowIndex(i1)
        val colu1 = GridPane.getColumnIndex(i1)
        val row2 = GridPane.getRowIndex(i2)
        val colu2 = GridPane.getColumnIndex(i2)
        GridPane.setRowIndex(i1, row2)
        GridPane.setColumnIndex(i1, colu2)
        GridPane.setRowIndex(i2, row1)
        GridPane.setColumnIndex(i2, colu1)
    }

    val task = object : TimerTask() {
        override fun run() {
            Platform.runLater {
                timeUsed.value = (System.currentTimeMillis() - start)/1000
            }
        }
    }
    init {
        reset()
        timer.schedule(task, 0, 100)
    }

    // nn : 8,15,24
    fun initView(nn: Int, smallSize: Double) {
        n = random(nn)              //自定义的函数,产生逆序数为偶数的不重复数组
        m = findnum(n)  //找出那个不在随机数组里面的数字
//        println(n.toList())
//        println(m)
        gridPane.clear()
        // nn1 =3-1,4-1,5-1
        val nn1 = sqrt(nn   1.0).toInt() - 1
        //        println("nn1:$nn1")

        var k = 0
        (0..nn1).forEach { i ->
            (0..nn1).forEach { j ->
                //切割图片
                imageViews[k].viewport = Rectangle2D(smallSize * j, smallSize * i, smallSize, smallSize)
                  k
            }
        }

        run {
            // nn1-1=2,3,4
            var t = 0
            (0..nn1).forEach { r ->
                (0..nn1).forEach { c ->
                    if (t < nn) {
//                    println("$t,$c,$r")
                        gridPane.add(imageViews[n[t]], c, r)
                    }
                      t
                }
            }
        }

        smallImage.value = imageViews[m].image
        smallImageView.viewport = imageViews[m].viewport

        imageViews[m].image = Image(imgBlankPath, smallSize, smallSize, false, true) //2.png为一个透明图,放在空格子中
        gridPane.add(imageViews[m], nn1, nn1)
    }

    //读取类路径下的图片
    fun initImageViews(nn: Int, imgPath: String): Array<ImageView> {
        return (1..nn).map {
            imageview(Image(imgPath, imageSize, imageSize, false, true)) {
                setOnMouseClicked(swapListener)
            }
        }.toTypedArray()
    }

    //判断是否拼成功
    fun issucc(imageViews: Array<ImageView>): Boolean {
        val t = imageViews.size
        val sqrtt = sqrt(t.toDouble()).toInt()
        imageViews.indices.forEach { i ->
            if (i != sqrtt * GridPane.getRowIndex(imageViews[i])   GridPane.getColumnIndex(imageViews[i])) {
                return false
            }
        }
        return true
    }

    // j=IntArray.size 1, IntArray比imageViews少一个元素
    fun findnum(n: IntArray): Int {
        (0..n.size).forEach { j ->
            if (j in n) {
            } else {
                return j
            }
        }
        return -1 //如果返回-1,则会造成imageViews数组越界
    }

    /**
     * println(random(8).toList()),output: [3, 6, 5, 4, 7, 1, 2, 0],or [2, 5, 6, 4, 1, 0, 3, 0]
     */
    //生成nn个不重复的逆序数为偶数的数字,比imageViews少一个元素,nn=N.value-1
    fun random(nn: Int): IntArray {
        var ran = IntArray(nn)
        while (!iso(ran)) {
            ran = random_num(nn)
        }
        return ran
    }

    //生成nn个不重复数,比imageViews少一个元素,nn=N.value-1
    fun random_num(nn: Int): IntArray {
        val r = IntArray(nn)
        val random = Random()
        var i = 0
        while (i < nn) {
            r[i] = random.nextInt(nn   1)
            for (j in 0 until i) {
                while (r[i] == r[j]) {
                    i--
                    break
                }
            }
              i
        }
        return r
    }

    //判断逆序数是否为偶数
    fun iso(num: IntArray): Boolean {
        var sum = 0
        val t = num.size
        (0..t - 2).forEach { i ->
            (i until t).forEach { j ->
                if (num[i] > num[j]) {
                    sum  
                }
            }
        }
        return sum % 2 == 0 && sum != 0
    }
}

0 人点赞