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

本文介绍了一个使用JavaFX和Kotlin开发的拼图游戏应用。游戏实现了动态选择拼图难度,图片切割,拼图交互及成功判断等功能。通过自定义函数生成逆序数为偶数的不重复数组,确保了拼图的可解性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.cyy.game.pingtu

import javafx.application.Application
import javafx.beans.property.SimpleObjectProperty
import javafx.event.EventHandler
import javafx.geometry.Pos
import javafx.geometry.Rectangle2D
import javafx.scene.control.Alert
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 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>()
    val smallImage = SimpleObjectProperty<Image>()
    // 大图片宽度
    val imageSize = 300.0
    // 每个小方格宽度
    var smallSize = imageSize / sqrt(N.value.toDouble())

    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()) {
                        val imgPath = imgFile.first().toString().replace("\\", "/")
                        bigImage.value = Image(File(imgPath).inputStream())
                        imageViews=initImageViews1(N.value, imgPath)
                        initView(N.value, imgPath, smallSize,imageViews)
                    }
                }
            }

            togglegroup {
                listOf(4,9, 16, 25).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())
                }
            }

            button("重置") {
                action {
                    bigImage.value = Image(imgPath.value)

                    imageViews =initImageViews(N.value,imgPath.value)
                    initView(N.value, imgPath.value, smallSize,imageViews)
                }
            }
            paddingAll = 10
        }
        center = gridpane {
            alignment=Pos.CENTER

            gridPane = this
            isGridLinesVisible = true
        }
        right = vbox(20) {
            alignment=Pos.CENTER

            rbox = this
            //显示空格子的图片
            imageview(smallImage) {
                smallImageView = this
            }
            //完整的大图
            imageview(bigImage) {
                bigImageView = this
                fitHeight = imageSize
                fitWidth = imageSize
            }
        }
    }

    inner class Myevent : EventHandler<MouseEvent> {               //点击事件的实现
        override fun handle(arg0: MouseEvent) {
            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
            if (dispx == -smallSize && dispy == 0.0) {               //点击的空格左边的格子
                swapimg(img, imageViews[m])             //交换imageView
                if (issucc(imageViews)) {                          //判断是否拼成功
                    val alert = Alert(Alert.AlertType.WARNING, "成功!")
                    alert.show()
                }
            } else if (dispx == 0.0 && dispy == -smallSize) {        //上面的格子
                swapimg(img, imageViews[m])
                if (issucc(imageViews)) {
                    val alert = Alert(Alert.AlertType.WARNING, "成功!")
                    alert.show()
                }
            } else if (dispx == smallSize && dispy == 0.0) {               //右边的格子
                swapimg(img, imageViews[m])
                if (issucc(imageViews)) {
                    val alert = Alert(Alert.AlertType.WARNING, "成功!")
                    alert.show()
                }
            } else if (dispx == 0.0 && dispy == smallSize) {                //下面的格子
                swapimg(img, imageViews[m])
                if (issucc(imageViews)) {
                    val alert = Alert(Alert.AlertType.WARNING, "成功!")
                    alert.show()
                }
            }
        }

        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)
        }
    }

    init {
        imageViews =initImageViews(N.value,imgPath.value)
        initView(N.value, imgPath.value, smallSize,imageViews)
    }

    // nn : 9,16,25
    fun initView(nn: Int, imgPath: String, smallSize: Double,imageViews:Array<ImageView>) {
        n = random(nn)              //自定义的函数,产生逆序数为偶数的不重复数组
        m = findnum(n)  //找出那个不在随机数组里面的数字
        gridPane.clear()
        // nn1 =3,4,5
        val nn1 = sqrt(nn.toDouble()).toInt()
        var i = 0
        var k = 0
        while (i <= nn1 - 1) {
            var j = 0
            while (j <= nn1 - 1) {
                //切割图片
                imageViews[k].viewport = Rectangle2D(smallSize * j, smallSize * i, smallSize, smallSize)
                ++j
                ++k
            }
            ++i
        }

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

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

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

    //读取类路径下的图片
    fun initImageViews(nn: Int, imgPath: String):Array<ImageView>{
        return (1..nn).map {
            imageview(imgPath) {
                onMouseClicked = Myevent()
            }
        }.toTypedArray()
    }
    //读取电脑文件系统中图片文件的路径
    fun initImageViews1(nn: Int, imgPath: String):Array<ImageView>{
        return (1..nn).map {
            imageview(Image(File(imgPath).inputStream())) {
                onMouseClicked = Myevent()
            }
        }.toTypedArray()
    }

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

    fun findnum(n: IntArray): Int {
        (n.indices).forEach { j ->
            if (j in n) {
            } else {
                return j
            }
        }

        return -1
    }

    //生成nn个不重复的逆序数为偶数的数字
    fun random(nn: Int): IntArray {
        var ran = IntArray(nn)
        while (!iso(ran)) {
            ran = random_num(nn)
        }
        return ran
    }

    //生成nn个不重复数
    fun random_num(nn: Int): IntArray {
        val r = IntArray(nn)
        val random = Random()
        var i = 0
        while (i < nn - 1) {
            r[i] = random.nextInt(nn)
            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
        for (i in 0..t - 2) {
            for (j in i..t - 1) {
                if (num[i] > num[j]) {
                    sum++
                }
            }
        }
        return sum % 2 == 0 && sum != 0
    }
}

 

转载于:https://my.oschina.net/u/3820046/blog/3098919

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值