【问题标题】:How to design a state flow for a react/redux flashcard game?如何为 react/redux 抽认卡游戏设计状态流?
【发布时间】:2016-05-15 04:41:10
【问题描述】:

这是我的第一个 redux 项目。我正在尝试制作一个可以教你平假名/片假名的抽认卡游戏。我陷入了一些状态流程......

游戏状态可视化

{
  // the current displayed card
  currentCard: null | {q: 'き', a: 'ki'},

  // time the game was started
  timestamp: millisecondTimestamp,

  // submitted answers
  answers: [
    {
      card: {q: 'か', a: 'ka'},
      answer: 'ka'
    },
    {
      card: {q: 'な', a: 'na'},
      answer: 'na'
    },
    ...
  ],

  // remaining cards in the deck
  deck: [
    {q: 'く', a: 'ku'},
    {q: 'け', a: 'ke'},
    {q: 'こ', a: 'ko'}
    ...
  ]
}

stores/game.js

import {BEGIN_GAME, NEXT_CARD, SUBMIT_ANSWER} from '../actions/';
import {isEmpty, first, rest} from '../utilities/';

const gameReducer = (state=null, action) => {
  switch (action.type) {

    case BEGIN_GAME:
      return Object.assign({}, state, {deck: action.deck});

    case NEXT_CARD:
      return isEmpty(state.deck)
        ? Object.assign({}, state, {
          currentCard: null
        })
        : Object.assign({}, state, {
          currentCard: first(state.deck),
          deck: rest(state.deck)
        })
      ;

    case SUBMIT_ANSWER:
      return Object.assign({}, state, {
        currentCard: null,
        answers: state.answers.concat({
          card: state.currentCard,
          answer: action.answer
        })
      });

    default:
      return state;
  }
};

export default gameReducer;

actions/index.js

import {shuffle} from '../utilities/';

export const BEGIN_GAME = 'BEGIN_GAME';
export const NEXT_CARD = 'NEXT_CARD';
export const SUBMIT_ANSWER = 'SUBMIT_ANSWER';

export const beginGame = deck => ({
  type: BEGIN_GAME,
  deck: shuffle(deck)
});

export const nextCard = () => ({
  type: NEXT_CARD
});

export const submitAnswer = answer => ({
  type: SUBMIT_ANSWER,
  answer
});

我认为游戏循环会像这样工作:

  • 当state 为null 时,等待用户按下开始游戏 按钮,该按钮调度BEGIN_GAME 动作
  • 此时,开始主游戏循环
  • 发送CHECK_END_GAME 以确定牌组中是否还有牌
  • 发送 NEXT_CARD 以显示牌组中的下一张牌
  • 等待点击答案,然后发送 SUBMIT_ANSWER 操作
  • 记录答案后,重复循环,从CHECK_END_GAME开始,接着是NEXT_CARD,接着是等待SUBMIT_ANSWER等
  • 在某些时候CHECK_END_GAME 应该触发DISPLAY_RESULTS 操作... ??

看,我有点迷路了...使用 redux,我认为我不能(或不应该)从商店 in 调度另一个动作。但是我该如何分解这些动作或状态检查呢?

我的大脑目前正在使用这样的模型进行思考:

BEGIN_GAME(deck)

LOOP(
  CHECK_END_GAME()->NEXT_CARD()->SUBMIT_ANSWER(answer)->
)

DISPLAY_RESULTS()

问题是,我不知道如何链接这样的操作,或者是否建议这样做。我愿意以完全不同的方式设计动作/商店。我是这里的菜鸟,所以如果我做错了什么,请告诉我。

任何向正确方向轻推都会有所帮助。谢谢!

【问题讨论】:

  • 我建议你用Redux Saga描述控制流,并使用Redux主要用于更新状态而不是控制流逻辑。 Redux Saga 让您可以使用 take 和 put Redux 操作的 ES6 生成器编写游戏循环。
  • @DanAbramov 我很高兴收到您的回复!我一定会去看看 ^__^
  • @DanAbramov 所以我已经阅读了一些有关 Saga 模式的内容,观看了 Redux Saga 文档中的 Applying Saga Pattern video,并使用了一些 Redux Saga 示例。在我继续尝试使用它之前,我只想明确一点,我的游戏是一个纯粹的本地/同步游戏,除了在第一场比赛之前异步加载一次完整的甲板,并在比赛结束后异步提交统计数据.
  • @DanAbramov 似乎 Saga 模式可能更适合具有高度异步行为的应用程序。如果您仍然认为它适用,您认为您可以为我提供一个起点吗?我将不胜感激。
  • 是的,也许。我还没有深入阅读您的应用程序的功能,这就是为什么我留下评论而不是答案的原因。 Sagas 是异步的,因为用户输入是异步的。不确定这是否对您有帮助。我认为您的问题最大的问题是我不明白您发布的代码是否有效,或者您是否有问题。有点不清楚。你搞定了吗?

标签: javascript reactjs redux


【解决方案1】:

对应用程序进行编码的一种方法是根据 Petri 网元素对游戏逻辑进行建模(或组织),并在此组织的基础上编写代码(Chionglo,2014 年)。以下是根据 Petri Nets(“如何设计”,2016 年)组织的游戏逻辑(基于抽认卡游戏)的示例。在这个游戏中有九张牌。一次显示一张牌,然后玩家猜牌。正确猜测的次数和错误猜测的次数被记录下来并继续游戏,直到没有更多可显示的牌为止。

图1(for the PDF version of this reply)是一个互动的、动态的游戏逻辑图;它包括带有标准 Petri 网符号和 Petri 网元素的一些高级符号的卡片的“图形符号”。游戏逻辑演示模式从牌堆中随机选择一张牌,然后随机猜牌。

参考文献

Chionglo, J. F. (2016)。对“如何为 react/redux 抽认卡游戏设计状态流?”的回复在堆栈溢出。堆栈溢出。 https://www.academia.edu/34059934/A_Reply_to_How_to_design_a_state_flow_for_a_react_redux_flashcard_game_at_Stack_Overflow.

Chionglo, J. F. (2014)。计算机编程的网络元素和注释:PDF 中的计算和交互。 https://www.academia.edu/26906314/Net_Elements_and_Annotations_for_Computer_Programming_Computations_and_Interactions_in_PDF.

“如何为 react/redux 抽认卡游戏设计状态流?” (2016 年)。堆栈交换。检索于 2016 年 2 月 7 日http://stackoverflow.com/questions/35220169/how-to-design-a-state-flow-for-a-react-redux-flashcard-game。

【讨论】:

  • 非常感谢您的学术回复。我不能说我以前曾经对我的一个问题得到过如此详细的答复。我以前从未听说过 Petri Net Elements,所以这对我来说有点学习曲线,但这一切看起来都很有趣。我一定会跟进最终对我有用的任何解决方案。
  • 不客气。我希望这些信息对您有用且实用。祝你的项目好运,让我们随时了解情况。
  • 由于文件位置的变化,此回复中的某些引用不正确。我没有编辑这个回复,而是创建了这个回复的新版本,它可以在academia.edu/34115722/… 获得。
【解决方案2】:

免责声明:这个回答会有点仓促,我要赶火车

目前,我只“链接”异步操作,但我想同样的原则也适用。

我不太了解这个游戏:P,但怀疑你应该多想一下:

BEGIN_GAME(deck)

DISPLAY_STATE
    {if END_GAME()}
        Its over!
    {else}
        <button onclick=dispatch( drawCardAndModifyStateAction )/>

在您的父级减速器中,您可以组合(而不是链式)您的减速器:

const gameReducer = (state=null, action) => {
  switch (action.type) {
    NEXT_CARD: state.nextCard = nextCard(state);
  }
  state.checkEndGame = checkEndGame(state);
  return state;
}

你的nextCard“sub”减速器做了一些事情......

const nextCard = (state=null, action) => {
  switch (action.type) {
    case NEXT_CARD:
        return Object.assign({}, state, {
          currentCard: first(state.deck),
          isEmpty: isEmpty(state.deck),
          deck: rest(state.deck)
        })
    }
  return state;
}

后跟checkEndGame

const checkEndGame = (state=null, action) => {
  switch (action.type) {
    default:
        return Object.assign({}, state, {
          message: ...,
          winner: ...,
        })
    }
}

您还可以研究redux saga、redux thunk 等中间件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    • 2017-08-17
    • 1970-01-01
    • 2016-09-14
    • 2021-03-26
    相关资源
    最近更新 更多