AB猜数字游戏


这是我的第一篇博客,没什么目的,就随便写的,权当联系如何写blog了。

猜数字游戏

想起小时候在文曲星上玩的猜数字游戏,摸鱼的时候就自己做了一个。
目前没做UI上的美化,仅实现了基础功能。

游戏说明

游戏规则很简单,程序随机生成一个四位数的数字(不包含重复数字),然后由玩家进行猜测。
如果猜的数字对了但位置不对,记B+1;如果猜的数字对了且位置也对,记A+1。都猜对时结果即4A0B。

项目设计

采用了Jetpack的LiveData和ViewModel,实现数据和UI分离。
贴上代码,具体的内容都写在注释里了。

代码实现

  1. MainActivity:
	public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    	private static final String TAG = "MainActivity";
    	private TextView textView;
    	private TextView result;
    	private GuessViewModel guessViewModel;	
    	private MutableLiveData<String> textLiveData;			//	每次按键的内容
    	private MutableLiveData<String> resultLiveData;		//  每次猜得结果的内容(结果记录板) 
   		private MutableLiveData<Boolean> canTouchLiveData;		//按键是否允许点击
		private Button button_0, button_1, button_2, button_3, button_4, button_5, button_6,
            button_7, button_8, button_9, button_del, button_guess;
   		private ScrollView scrollView;

	    @Override
	    protected void onCreate(Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	        setContentView(R.layout.activity_main);
	
	        init();
	
	        textLiveData.observeForever(s -> {
	        	//获取显示板上已有的文本内容
	            String t = textView.getText().toString();
	            //如果已显示四位数字了,则不在允许新的数字
	            if (t.length() < 4) {
	                textView.setText(t + s);
	            }
	        });
	        
			// 将猜得的结果显示在结果记录板上
	        resultLiveData.observeForever(s -> result.setText(s));
	
			// 如果已猜中,则禁止点击除“重新开始”外的其他按键
	        canTouchLiveData.observeForever(b -> {
	            button_0.setEnabled(b);
	            button_1.setEnabled(b);
	            button_2.setEnabled(b);
	            button_3.setEnabled(b);
	            button_4.setEnabled(b);
	            button_5.setEnabled(b);
	            button_6.setEnabled(b);
	            button_7.setEnabled(b);
	            button_8.setEnabled(b);
	            button_9.setEnabled(b);
	            button_del.setEnabled(b);
	            button_guess.setEnabled(b);
	        });
	    }
	
	    /**
	    *	删除最后一位数字
	    */
	    private void del() {
	    	// 获取显示板上的数字
	        String t1 = textView.getText().toString();
	        // 如果无数字则return掉
	        if (t1.length() == 0 || t1 == null) return;
	        // 截取字符串,去掉最后一位
	        textView.setText(t1.substring(0, t1.length() - 1));
	    }
	
	    /**
	    *	清空结果记录板
	    */
	    private void clear() {
	        textView.setText("");
	    }
		
		/**
		*	初始化View
		*/
	    private void init() {
	    	//获取ViewModel
	        guessViewModel = new ViewModelProvider(this,
	                new ViewModelProvider.AndroidViewModelFactory(getApplication()))
	                .get(GuessViewModel.class);
	        //获取各LiveData
	        textLiveData = guessViewModel.getTextLiveData();
	        resultLiveData = guessViewModel.getResultLiveData();
	        canTouchLiveData = guessViewModel.getCanTouchLiveData();
	
	        textView = findViewById(R.id.tv_input);
	        result = findViewById(R.id.tv_result);
	        button_0 = findViewById(R.id.number_0);
	        button_0.setOnClickListener(this);
	        button_1 = findViewById(R.id.number_1);
	        button_1.setOnClickListener(this);
	        button_2 = findViewById(R.id.number_2);
	        button_2.setOnClickListener(this);
	        button_3 = findViewById(R.id.number_3);
	        button_3.setOnClickListener(this);
	        button_4 = findViewById(R.id.number_4);
	        button_4.setOnClickListener(this);
	        button_5 = findViewById(R.id.number_5);
	        button_5.setOnClickListener(this);
	        button_6 = findViewById(R.id.number_6);
	        button_6.setOnClickListener(this);
	        button_7 = findViewById(R.id.number_7);
	        button_7.setOnClickListener(this);
	        button_8 = findViewById(R.id.number_8);
	        button_8.setOnClickListener(this);
	        button_9 = findViewById(R.id.number_9);
	        button_9.setOnClickListener(this);
	        button_del = findViewById(R.id.bt_delete);
	        button_del.setOnClickListener(this);
	        button_guess = findViewById(R.id.bt_guess);
	        button_guess.setOnClickListener(this);
	        findViewById(R.id.bt_again).setOnClickListener(this);
	        scrollView = findViewById(R.id.scroll_view);
	        // 才出正确答案的回调接口
	        guessViewModel.setOnSuccessListener(() -> Log.d(TAG, "success: !!!!"));
	    }
	
	    @Override
	    public void onClick(View view) {
	        switch (view.getId()) {
	            case R.id.number_0:
	            case R.id.number_1:
	            case R.id.number_2:
	            case R.id.number_3:
	            case R.id.number_4:
	            case R.id.number_5:
	            case R.id.number_6:
	            case R.id.number_7:
	            case R.id.number_8:
	            case R.id.number_9:
	            	// 获取点击的数字
	                String s = ((Button) view).getText().toString();
	                // 判断是否和之前的数字重复
	                if (Utils.hasRepeatNumber(textView.getText().toString(), s)) return;
	                // 将数字更新至显示板上
	                textLiveData.setValue(s);
	                break;
	            case R.id.bt_delete:
		            // 删除最后一位数字
	                del();
	                break;
	            case R.id.bt_guess:
	            	// 执行ViewModel中的检查结果方法
	                guessViewModel.checkResult(textView.getText().toString());
	                // 清除显示板上的内容
	                clear();
	                // 将ScrollView自动翻至最底部
	                new Handler().post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN));
	                break;
	            case R.id.bt_again:
	            	//重新开始
	            	// 清除显示板上的内容
	                clear();
	                // 创建一个新的答案
	                guessViewModel.makeAnswer();
	                break;
	            default:
	                break;
	        }
	    }
	}
  1. GuessViewModel.java
public class GuessViewModel extends AndroidViewModel {
    private static final String TAG = "GuessViewModel";
    private static final int NUMBER_LENGTH = 4;		//因为游戏是四位数的猜数字,所以定义了常量4,避免魔法数
    private MutableLiveData<String> textLiveData;
    private MutableLiveData<String> resultLiveData;
    private MutableLiveData<Boolean> canTouchLiveData;
    private String answer;		//生成的答案
    private Context mContext;
    private int count;			//每局猜测的次数

    public GuessViewModel(@NonNull Application application) {
        super(application);
        //使用的是AndroidViewModel,方便获取Context
        mContext = getApplication().getApplicationContext();
    }

    public MutableLiveData<String> getTextLiveData() {
        if (textLiveData == null) {
            textLiveData = new MutableLiveData<>();
            textLiveData.setValue("");
        }
        return textLiveData;
    }

    public MutableLiveData<String> getResultLiveData() {
        if (resultLiveData == null) {
            resultLiveData = new MutableLiveData<>();
            resultLiveData.setValue("");
        }
        return resultLiveData;
    }

    public MutableLiveData<Boolean> getCanTouchLiveData() {
        if (canTouchLiveData == null) {
            canTouchLiveData = new MutableLiveData<>();
            canTouchLiveData.setValue(true);
        }
        return canTouchLiveData;
    }

    /**
    *	检查结果
    *	@param result 玩家猜测的结果
    */
    public void checkResult(String result) {
		//如果还没有答案,则生成一个答案
        if (answer == null) {
            answer = makeAnswer();
        }
        Log.d(TAG, "answer is : " + answer);
        //创建一个HashSet,用来判断一共猜中了几个数字
        HashSet set = new HashSet();
		//将玩家猜的结果和答案都转换成char数组
        char[] charResult = result.toCharArray();
        char[] charAnswer = answer.toCharArray();
		
        int a = 0;  //位置和数字都正确
        int b = 0;  //仅数字正确

        //输入数字位数不足
        if (result == null || result.length() != NUMBER_LENGTH) {
            Toast.makeText(mContext, "请输入正确数字", Toast.LENGTH_SHORT).show();
            return;
        }
		//先将玩家猜出的结果装入set中
        for (int i = 0; i < NUMBER_LENGTH; i++) {
            set.add(charResult[i]);
        }
        //判断结果
        for (int i = 0; i < NUMBER_LENGTH; i++) {
            //1.比较有几个相同的数字
            if (set.contains(charAnswer[i])) {
                ++b;
            }
            //2.比较有几个位置相同的数字
            if (charResult[i] == charAnswer[i]) {
                ++a;
            }
        }
        // b中包含了a,故需要减去a
        b -= a;

        //完成一次猜测,计数器加一
        ++count;
        //更新记录板
        addLog(String.format("%d .  %s  %dA%dB", count, result, a, b));

        //完全猜中,游戏结束
        if (a == 4) {
            if (onSuccessListener != null) {
                onSuccessListener.success();
            }
            //已经猜中了,游戏结果,禁止点击按键
            canTouchLiveData.setValue(false);
        }
    }

	/**
	* 更新结果记录板
	* param s 最后一个猜测的结果
	*/
    private String addLog(String s) {
        String origin = resultLiveData.getValue();
        String newText = origin + "\r\n" + s;
        resultLiveData.setValue(newText);
        return newText;
    }

	/**
    * 生成题目,即开始新的一局
	*/
    public String makeAnswer() {
        //允许按键
        canTouchLiveData.setValue(true);
        //计数器清空
        count = 0;
        //记录板清空
        resultLiveData.setValue("");
        //生成一个随机数 0 至 9876 的随机数。(大于9876的数字必然包含重复数字)
        Random random = new Random();
        int result = random.nextInt(9876);
        //将随机数转成字符串形式
        String answer = String.valueOf(result);

        //数字小于三位数,重新生成。
        if (result < 123) {
        	//递归调用
            answer = makeAnswer();
        }
        //是三位数,需在结果前加 0
        if (Utils.inRange(123, 987, result)) {
            answer = "0" + answer;
        }
        //如果数字有重复,重新生成
        if (Utils.hasRepeatNumber(answer)) {
        	//递归调用
            answer = makeAnswer();
        }
        //将answer给到外面,以便其他方法使用
        this.answer = answer;
        return answer;
    }

	// 游戏猜中的回调
    public interface OnSuccessListener {
        void success();
    }
    private OnSuccessListener onSuccessListener;
    public void setOnSuccessListener(OnSuccessListener onSuccessListener) {
        this.onSuccessListener = onSuccessListener;
    }

}
  1. Utils.java
//工具类
public class Utils {
	 /**
     * 判断要选择的数字是否和已输入的数字重复
     * @param s0 原有的数字串
     * @param s1 新输入的数字
     * @return 
     */
    public static boolean hasRepeatNumber(String s0, String s1) {
        char[] chars = s0.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == s1.toCharArray()[0]) {
                return true;
            }
        }
        return false;
    }

    public static boolean inRange(int start, int end, int number) {
        return Math.max(start, number) == Math.min(end, number);
    }

    public static boolean hasRepeatNumber(String s) {
        HashSet set = new HashSet();
        char[] chars = s.toCharArray();
        int len = chars.length;
        for (int i = 0; i < len; i++) {
            set.add(chars[i]);
        }
        return set.size() != len;
    }
}
  1. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_input"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:letterSpacing="1"
        android:textSize="30dp"
        app:layout_constraintBottom_toTopOf="@id/ll_input_numbers"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/bt_delete"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="1234" />

    <Button
        android:id="@+id/bt_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="DEL"
        android:textSize="26dp"
        app:layout_constraintBottom_toBottomOf="@id/tv_input"
        app:layout_constraintLeft_toRightOf="@id/tv_input"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@id/tv_input" />

    <LinearLayout
        android:id="@+id/ll_input_numbers"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="30dp"
        android:orientation="horizontal"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_input">

        <Button
            android:id="@+id/number_0"
            style="@style/NumberButton"
            android:text="0" />

        <Button
            android:id="@+id/number_1"
            style="@style/NumberButton"
            android:text="1" />

        <Button
            android:id="@+id/number_2"
            style="@style/NumberButton"
            android:text="2" />

        <Button
            android:id="@+id/number_3"
            style="@style/NumberButton"
            android:text="3" />

        <Button
            android:id="@+id/number_4"
            style="@style/NumberButton"
            android:text="4" />

        <Button
            android:id="@+id/number_5"
            style="@style/NumberButton"
            android:text="5" />

        <Button
            android:id="@+id/number_6"
            style="@style/NumberButton"
            android:text="6" />

        <Button
            android:id="@+id/number_7"
            style="@style/NumberButton"
            android:text="7" />

        <Button
            android:id="@+id/number_8"
            style="@style/NumberButton"
            android:text="8" />

        <Button
            android:id="@+id/number_9"
            style="@style/NumberButton"
            android:text="9" />
    </LinearLayout>

       <Button
        android:id="@+id/bt_guess"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Guess !"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/bt_again"
        app:layout_constraintTop_toBottomOf="@id/ll_input_numbers" />

    <Button
        android:id="@+id/bt_again"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Again !"
        app:layout_constraintLeft_toRightOf="@id/bt_guess"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/ll_input_numbers" />

    <ScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:paddingHorizontal="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/bt_guess">

        <TextView
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            tools:text="1A1B" />
    </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数字  数字   数字可以算是一种益智类小游戏,一般两个人玩,也可以由一个人和电脑玩,可以在纸上、在网上都可以玩。这种游戏规则简单,但可以考验人的严谨和耐心。   目录   1 规则   1.1 次数限制   1.2 含重复数字数字   2 解法   2.1 计算机解   2.2 推理解   2.3 代入解   2.4 其他   3 参看   规则   这个游戏的规则比较简单,一般两个人玩,一方出数字,一方。出数字的人要想好一个没有重复数字的4位数,不能让得人知道。的人就可以开始。每一个数字,出数者就要根据这个数字给出几A几B,其中A前面的数字表示位置正确的数的个数,而B前的数字表示数字正确而位置不对的数的个数。   如正确答案为5234,而的人5346,则是1A2B,其中有一个5的位置对了,记为1A,而3和4这两个数字对了,而位置没对,因此记为2B,合起来就是1A2B。   接着的人再根据出题者的几A几B继续,直到中为止。   次数限制   有的时候,这个游戏测次数上的限制。根据计算机测算,这个游戏,如果以最严谨的计算,任何数字可以在7次之内出。而有些地方把次数限制为6次或更少,则会导致有些数可能不出来。而有些地方考虑到人的逻辑思维难以达到计算机的那么严谨,故设置为8次甚至10次。也有的没有次数上的限制。   含重复数字数字   有一种使用范围比较狭窄的数字,是允许重复数字存在的数字,但由于其规则较复杂,故没有得到广泛的推广。其规则如下:   除了上面的规则外,如果有出现重复的数字,则重复的数字每个也只能算一次,且以最优的结果为准,   如正确答案为5543,的人5255,则在这里不能认为测的第一个5对正确答案第二个,根据最优结果为准的原理和每个数字只能有一次的规则,两个比较后应该为1A1B,第一个5位子正确,记为1A;数字中的第三个5或第四个5和答案的第二个5匹配,只能记为1B。当然,如果有5267中的第一个5不能与答案中的第二个5匹配,因此只能记作1A0B。   解法   对于不同的人,常常会用到不同的解法   计算机解   通常采用的计算机解是通过排除法,即遍历所有可能的数,将不符合要求的数剃掉。   下面是一个计算机处理的例子:   for (int i = 0; i < Array.Count; i++) { if (Array与当前输出数字的比较 != 用户输入的与正确答案对比的结果) { Array.Remove(i); i--; } }      这个代码采用C#的语法,其中Array表示所有可能的数字的集合。这个例子为了方便说明,结合了语言的描述。   这样的方法充分利用了计算机计算速度快的优势,迅速排出不符合要求的数。通常第一次测的时间(有的引擎为第二次测)会在10秒左右,而随着测次数的不断增加,测的时间会越来越短,最后几乎不需要时间,这是由于集合中的数越来越少,排除需要的时间也随之减少。   推理解   计算机解释根据这种方法推广的。这种解法的中心思想是假设的这个数字是正确答案,即如果它为正确答案,那么这个数应该符合已经测的数及其结果。如已经有   1234 0A0B   那么下一步就不能含有1234中任一数字的数,因为如果正确答案含1234中任一,结果就不可能为0A0B。   这种解法对者要求较高,通常,可能会被定式思维所干扰,导致难以出。   基于这个解法,根据个人思维风格和起始数字选择的不同,以及对出题者出数风格的测,有时可以把测次数控制在5步内,但不总能在5步内出。   使用这种解法需要考虑的时间很久,和计算机解正好相反,人使用这种方法,通常随着测次数的增加,需要考虑的东西不断增多,反而考虑的时间会变得越来越长。   代入解   还有一种方法,在人的测中很常用,即将推理出不可能含有的数字,代入,察看那些数字是有的。   但这种方法其测次数难以确定,且通常的测次数比推理解多。   其他   可能还有其他的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值