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

Stella981
• 阅读 622

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

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

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

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

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
    }
}
点赞
收藏
评论区
推荐文章
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Oracle ADG究竟是否收费?
!(https://oscimg.oschina.net/oscnet/faaf5c218b3045fe9a38ffa00c48a996.png)前两天微信群中,有位朋友问了,OracleDatabaseStandardEdition11gR2标准版支持activedataguard么?可能平时
Stella981 Stella981
3年前
Cocos Creator 如何制作拼图游戏,支持无规则形状!
预览效果!(https://oscimg.oschina.net/oscnet/c075e00adf85d09d261e7006e2eeeef3065.gif)  实现思路  假设一张图,按照row行col列分成count(row\col) 份,由count份碎片组成,每个碎片有自己特定的
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Modelsim仿真一些简单问题
1.在学习时序逻辑的时候,我们明白寄存器赋值总有一拍的延迟但是有的时候仿真的时候会发现会没有延迟,比如下面这段代码对应的modelsim仿真。源代码代码如下:!(https://oscimg.oschina.net/oscnet/90dc0d40ac35ad54cae504f85c1ccc29b16.png)
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
mysql2_4
连接查询将多张表进行记录的连接(按照某个指定的条件进行数据拼接)  最终结果是:记录数有可能变化,字段书一定会增加(至少两张表的合并)  连接查询的意义:在用户查看数据的时候,需要显示的数据来自多张表。  连接查询分类sql中将连接查询分成四类:内连接,外连接,自然连接,交叉连接  1.交叉连接:crossjoi
Stella981 Stella981
3年前
300行代码不到的tornadofx拼图游戏
!(https://oscimg.oschina.net/oscnet/801dfa96d248ac5d1f9cea1911dad503058.jpg)packagecom.cyy.game.pingtuimportjavafx.application.Applicationimportjavafx.bea
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究