Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >300行代码不到的javafx框架tornadofx拼图游戏

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

原创
作者头像
用户6167008
修改于 2019-09-05 10:18:25
修改于 2019-09-05 10:18:25
1.4K0
举报

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

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

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

heart2circle.zip

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

代码语言:txt
AI代码解释
复制
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
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
javafx框架tornadofx画个心形
[zyv5mi8xkw.gif] import javafx.animation.AnimationTimer import javafx.application.Application import javafx.application.Platform import javafx.geometry.Pos import javafx.scene.canvas.GraphicsContext import javafx.scene.control.RadioButton import javafx.sce
用户6167008
2019/09/03
1.4K0
javafx框架tornadofx画个心形
javafx框架tornadofx实战-益智游戏-找出指定的内容1
找到它.gif 布局与上一个案例舒尔特基本相似,来体验一下吧,代码也在同小异,直接上代码: 重要的事情说3遍: 动手敲代码!!!动手敲代码!!!动手敲代码!!! class MainAPP : App(MainView::class) class MainView : View("把指定的图片全都给我找出来") { private val c by inject<MainController>() lateinit var r: GridPane private val sue
用户6167008
2020/08/11
7210
javafx框架tornadofx实战-益智游戏-找出指定的内容1
吃鱼小游戏,可以让小朋友练习键盘方向键和鼠标,采用javafx框架tornadofx实现
[ts8aa4beeu.gif] import javafx.application.Application import javafx.stage.Stage import javafx.scene.paint.Color import javafx.scene.text.Font import javafx.scene.text.FontWeight import javafx.animation.AnimationTimer import javafx.beans.Observable import
用户6167008
2019/09/04
1.1K0
吃鱼小游戏,可以让小朋友练习键盘方向键和鼠标,采用javafx框架tornadofx实现
GIRAFFE: CVPR 2021 最佳论文介绍和代码解释
GIRAFFE是一个基于学习的、完全可微的渲染引擎,用于将场景合成为多个“特征域”的总和。
deephub
2021/07/01
1.1K0
再来做个小工具,简易计算器,用到了jfinal enjoy,140行代码不到
要搭建好JDK8的环境,才能运行 [73y1k5cjic.gif] import com.jfinal.kit.Kv import com.jfinal.template.Engine import javafx.scene.layout.GridPane import tornadofx.* import kotlin.Exception import kotlin.math.pow import kotlin.math.sqrt class EnjoyApp : App(EnjoyView::clas
用户6167008
2019/09/30
6720
再来做个小工具,简易计算器,用到了jfinal enjoy,140行代码不到
javafx框架tornadofx实战-舒尔特1
set舒尔特.gif import javafx.animation.AnimationTimer import javafx.application.Platform import javafx.geometry.Orientation import javafx.geometry.Pos import javafx.scene.control.RadioButton import javafx.scene.layout.GridPane import javafx.scene.layout.Prior
用户6167008
2020/06/05
7671
javafx框架tornadofx实战-舒尔特1
来玩玩打地鼠游戏,300行代码不到
所用到的图片: 打地鼠.zip [mzijcqa487.gif] a2.gif 图1代码: import javafx.animation.KeyFrame import javafx.animation.Timeline import javafx.event.ActionEvent import javafx.event.EventHandler import javafx.geometry.Pos import javafx.scene.effect.DropShadow import javaf
用户6167008
2019/09/26
1.8K0
来玩玩打地鼠游戏,300行代码不到
javafx框架tornadofx实战-舒尔特8-增加统计功能
重要的事情说3遍: 动手敲代码!!!动手敲代码!!!动手敲代码!!! shuerte3.gif 新建StatisticView.kt文件,并添加到主视图的right drawer中: import javafx.scene.chart.* import tornadofx.* class StatisticView : View("统计") { private val c by inject<MainController>()//注入控制器 private var bc by singleA
用户6167008
2020/06/28
5401
javafx框架tornadofx实战-舒尔特8-增加统计功能
fast.ai 深度学习笔记(二)
learn — 这将显示我们在末尾添加的层。这些是我们在precompute=True时训练的层
ApacheCN_飞龙
2024/02/11
2600
fast.ai 深度学习笔记(二)
一颗心到一个圆的演变,javafx框架tornadofx动画演示
[or8lllfpxg.gif] import javafx.animation.AnimationTimer import javafx.application.Application import javafx.application.Platform import javafx.geometry.Pos import javafx.scene.canvas.GraphicsContext import javafx.scene.control.RadioButton import javafx.sce
用户6167008
2019/09/04
7810
一颗心到一个圆的演变,javafx框架tornadofx动画演示
javafx框架tornadofx入门实战23_treeview_advanced
treeview构建2棵有关联的文件(夹)树,通过tornadofx提供的populate,仅用3行代码实现一棵树。并可在右侧窗口查看文本文件的内容和图片 import com.dlsc.gemsfx
用户6167008
2020/03/31
2.3K1
javafx框架tornadofx入门实战23_treeview_advanced
一颗心到一个圆的演变,javafx框架tornadofx动画演示
采用runAsync和Thread.sleep(10)实现动画效果 import javafx.geometry.Pos import javafx.scene.canvas.GraphicsContext import javafx.scene.control.RadioButton import javafx.scene.paint.Color import javafx.scene.text.FontWeight import tornadofx.* import java.util.* import
用户6167008
2019/09/16
6630
手把手教你使用CanvasAPI打造一款拼图游戏
我们今天的目标是使用HTML5画布技术制作一款拼图小游戏,要求将图像划分为3*3的9块方块并打乱排序,用户可以移动方块拼成完整图片。
前端皮皮
2021/11/02
1.6K0
手把手教你使用CanvasAPI打造一款拼图游戏
javafx框架tornadofx批量生成桌面版CRUB程序
javafx框架tornadofx批量生成桌面版CRUB程序,采用tornadofx制作界面,jfinal active record操作数据库,jfinal enjoy模板引擎生成相关代码,只演示了简单桌面版CRUB程序,更完善的程序需要对模板代码进行设计。
用户6167008
2019/09/03
7640
二维码生成小工具,采用javafx框架tornadofx实现界面开发
[3zqf7zy13o.gif] import javafx.application.Application import javafx.beans.property.SimpleObjectProperty import javafx.embed.swing.SwingFXUtils import javafx.scene.image.Image import javafx.scene.image.WritableImage import tornadofx.* import javax.imageio.
用户6167008
2019/09/11
1.5K0
二维码生成小工具,采用javafx框架tornadofx实现界面开发
javafx框架tornadofx入门26_treeview_增加删除修改
treeview-add-delete-modify.gif 在上一篇的基础上,为treeview添加“增加删除修改”的功能,其中数据类Company,Department,Staff的代码在在上一篇中 import javafx.scene.control.TreeItem import javafx.scene.control.TreeView import javafx.scene.layout.Priority import tornadofx.* class MainView26 : View
用户6167008
2020/04/20
7700
javafx框架tornadofx入门26_treeview_增加删除修改
用javafx框架tornadofx演示分形图的绘制
用到了kotlin 协程、带方法的枚举类等知识点 [fqy75r4fs5.gif] import javafx.beans.property.SimpleObjectProperty import javafx.geometry.Orientation import javafx.scene.canvas.Canvas import javafx.scene.canvas.GraphicsContext import javafx.scene.control.TextFormatter import jav
用户6167008
2019/10/14
8670
用javafx框架tornadofx演示分形图的绘制
使用javafx框架tornadofx制作诗经查看工具
为了让各位都动起手来一起学习,以后的博客中贴一半代码,另一半代码以图片形式显示。用到的json数据
用户6167008
2019/12/02
6150
使用javafx框架tornadofx制作诗经查看工具
在tornadofx中使用javafx并发框架示例
1个枚举javafx.concurrent.Worker.State,代表Worker的不同状态READY,SCHEDULED,RUNNING,SUCCEEDED,CANCELLED,FAILED
用户6167008
2019/10/10
1.4K0
在tornadofx中使用javafx并发框架示例
javafx框架tornadofx演示jfinal enjoy模板引擎支持动态参数又可调用子模板中的函数
[s5hhy3i74u.gif] 首先根据https://www.jfinal.com/share/1991 新建类 MyRenderDirective 新建index.html #myRender(layoutPath) #@layout() #define content() 显示#(layoutPath) #end 新建layout.html #define layout() i am <span style="color:red">layout.html</span> <br> <br> #@c
用户6167008
2020/03/06
1.6K0
javafx框架tornadofx演示jfinal enjoy模板引擎支持动态参数又可调用子模板中的函数
推荐阅读
相关推荐
javafx框架tornadofx画个心形
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文