Sunday, 1 December 2019

Restrict cell selection per section in UITableView swift

I want to restrict certain sections of my tableview to only allow 1 cell to be selected, as of right now all my cells can be selected regardless of the section it's in

There is a little twist however : My sections are an [array] and change dynamically depending on different variables.

My sections are themselves each, a Variable, so I can pinpoint to them programmatically like this :

var section1 = [NSDictionary(objects: [NSLocalizedString("Alcohol use less than 24 hours", comment:""), 2],

As said before however, section1 might need to be restricted while section4 might not.

Some code from the tableView :

// checkmarks when tapped

    func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) {
            if self.selectedIndexPaths.contains(indexPath) {

                cell.accessoryType = .none
                cell.backgroundColor = UIColor.clear
                self.selectedIndexPaths.remove(indexPath)

                if CrewMembersNumber == "1" {
                    if((indexPath).section == 0) {
                        self.section1score -= section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section3score -= section3[(indexPath).row].object(forKey: "value") as! Int
                    }

                } else if CrewMembersNumber == "2" {
                    if((indexPath).section == 0) {
                        self.section1score -= section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section2score -= section2[(indexPath).row].object(forKey: "value") as! Int
                    }
                } else if CrewMembersNumber == "3" {
                    if((indexPath).section == 0) {
                        self.section1score -= section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section5score -= section5[(indexPath).row].object(forKey: "value") as! Int
                    }

                } else {
                    // if crewmemebernumber doest return 1-2 or 3
                    if((indexPath).section == 0) {
                        self.section1score -= section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section4score -= section4[(indexPath).row].object(forKey: "value") as! Int
                    }

                }

            } else {
                cell.accessoryType = .checkmark
                cell.backgroundColor = UIColor (red:236/255.0, green: 236/255, blue: 236/255, alpha: 1.0)
                self.selectedIndexPaths.add(indexPath)

                if CrewMembersNumber == "1" {
                    if((indexPath).section == 0) {
                        self.section1score += section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section3score += section3[(indexPath).row].object(forKey: "value") as! Int
                    }

                } else if CrewMembersNumber == "2" {
                    if((indexPath).section == 0) {
                        self.section1score += section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section2score += section2[(indexPath).row].object(forKey: "value") as! Int
                    }

                } else if CrewMembersNumber == "3" {
                    if((indexPath).section == 0) {
                        self.section1score += section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section5score += section5[(indexPath).row].object(forKey: "value") as! Int
                    }
                } else {
                    // if crewmemebernumber doest return 1-2 or 3
                    if((indexPath).section == 0) {
                        self.section1score += section1[(indexPath).row].object(forKey: "value") as! Int
                    } else if((indexPath).section == 1) {
                        self.section4score += section4[(indexPath).row].object(forKey: "value") as! Int
                    }
                }
            }
            self.updateToolbarAndLabel(self.totalScore)
            self.tableView.reloadData()
        }
    }


    func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        cell.textLabel!.text = self.textForIndexPath(indexPath);
        cell.textLabel!.font = UIFont(name:"Avenir", size:19)
        cell.textLabel!.numberOfLines = 0
        cell.selectionStyle = UITableViewCellSelectionStyle.none;

        if(self.selectedIndexPaths.contains(indexPath)) {

            cell.accessoryType = .checkmark;
            cell.backgroundColor = UIColor (red:236/255.0, green: 236/255, blue: 236/255, alpha: 1.0)
        } else {
            cell.accessoryType = .none;
            cell.backgroundColor = UIColor.clear
        }
        return cell
 }

Right now the code behaves the same in every section, and all cells are selectable no problem, I want to be able to select only 1 cell in section4 and section5

my question goes like this,

1st : how can I create a variable or something else that keeps track of which sections need to be restricted (like section4 section need to be restricted so I need a way to "flag" them)

2nd : how can I restrict a section to only allow for 1 cell to be selected (and automatically deselect the last one selected)

thanks



from Restrict cell selection per section in UITableView swift

Python chess minimax algorithm - How to play with black pieces (Bot has white)

Motivation:

I am trying to make a basic AI agent that can play chess against an opponent. The goal is to see how good it can become through the use of machine learning later on and also learn a the fine details in chess that are hidden from us when we just play it, such as evaluation parameters.


Code:

Here is what I have so far:

import chess, chess.pgn, time, math, io
import numpy as np 

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import Select

piece_values = {'P': 10, 'N': 30, 'B': 30, 'R': 50, 'Q': 90, 'K': 100, 'p': -10, 'n': -30, 'b': -30, 'r': -50, 'q': -90, 'k': -100}

# These are all flipped
position_values = {
        'P' : np.array([ [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0],
                        [5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0],
                        [1.0,  1.0,  2.0,  3.0,  3.0,  2.0,  1.0,  1.0],
                        [0.5,  0.5,  1.0,  2.5,  2.5,  1.0,  0.5,  0.5],
                        [0.0,  0.0,  0.0,  2.0,  2.0,  0.0,  0.0,  0.0],
                        [0.5, -0.5, -1.0,  0.0,  0.0, -1.0, -0.5,  0.5],
                        [0.5,  1.0, 1.0,  -2.0, -2.0,  1.0,  1.0,  0.5],
                        [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0] ]),

        'N' : np.array([[-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0],
                       [-4.0, -2.0,  0.0,  0.0,  0.0,  0.0, -2.0, -4.0],
                       [-3.0,  0.0,  1.0,  1.5,  1.5,  1.0,  0.0, -3.0],
                       [-3.0,  0.5,  1.5,  2.0,  2.0,  1.5,  0.5, -3.0],
                       [-3.0,  0.0,  1.5,  2.0,  2.0,  1.5,  0.0, -3.0],
                       [-3.0,  0.5,  1.0,  1.5,  1.5,  1.0,  0.5, -3.0],
                       [-4.0, -2.0,  0.0,  0.5,  0.5,  0.0, -2.0, -4.0],
                       [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0] ]),

        'B' : np.array([[-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0],
                       [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                       [-1.0,  0.0,  0.5,  1.0,  1.0,  0.5,  0.0, -1.0],
                       [-1.0,  0.5,  0.5,  1.0,  1.0,  0.5,  0.5, -1.0],
                       [-1.0,  0.0,  1.0,  1.0,  1.0,  1.0,  0.0, -1.0],
                       [-1.0,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0, -1.0],
                       [-1.0,  0.5,  0.0,  0.0,  0.0,  0.0,  0.5, -1.0],
                       [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0] ]),

        'R' : np.array([[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,  0.0],
                       [ 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,  0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                       [ 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0,  0.0]]),

        'Q' : np.array([[-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0],
                       [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                       [-1.0,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                       [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                       [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                       [-1.0,  0.5,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                       [-1.0,  0.0,  0.5,  0.0,  0.0,  0.0,  0.0, -1.0],
                       [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0]]),

        'K' : np.array([[ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                       [ -2.0, -3.0, -3.0, -4.0, -4.0, -3.0, -3.0, -2.0],
                       [ -1.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -1.0],
                       [  2.0,  2.0,  0.0,  0.0,  0.0,  0.0,  2.0,  2.0 ],
                       [  2.0,  3.0,  1.0,  0.0,  0.0,  1.0,  3.0,  2.0 ]])}

class LichessBot:
    def __init__(self, fen):
        self.fen = fen
        self.bot = webdriver.Firefox(executable_path=r'geckodriver.exe')

    def initialize(self):
        bot = self.bot
        bot.get('https://lichess.org/editor/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR_w_KQkq_-')
        time.sleep(3)
        analysis = bot.find_element_by_css_selector(".actions > a:nth-child(2)").click()
        time.sleep(1)

    def gameSelect(self, fen):
        bot = self.bot

        fen_area = bot.find_element_by_class_name("analyse__underboard__fen")
        bot.execute_script('arguments[0].setAttribute("value", arguments[1]);', fen_area, fen)

        # Refresh the page to enter new fen number properly every time
        fen_new = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value').replace(' ', '_')
        bot.get('https://lichess.org/analysis/standard/{}'.format(fen_new))

    def gameReturn(self):
        bot = self.bot

        fen_return = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value')
        time.sleep(1)
        return fen_return

def positionEvaluation(position, piece_values=piece_values, position_values=position_values):
    # Position of pieces is not taken into account for their strength
    if position_values == 'None':
        total_eval = 0
        pieces = list(position.piece_map().values())

        for piece in pieces:
            total_eval += piece_values[str(piece)]

        return total_eval

    else:
        positionTotalEval = 0
        pieces = position.piece_map()

        for j in pieces:
            file = chess.square_file(j)
            rank = chess.square_rank(j)

            piece_type = str(pieces[j])
            positionArray = position_values[piece_type.upper()]

            if piece_type.isupper():
                flippedPositionArray = np.flip(positionArray, axis=0)
                positionTotalEval += piece_values[piece_type] + flippedPositionArray[rank, file]

            else:
                positionTotalEval += piece_values[piece_type] - positionArray[rank, file]

        return positionTotalEval

def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
    if depth == 0 or position.is_game_over():
        return positionEvaluation(position, piece_values, position_values), bestMove

    if maximizingPlayer:
        maxEval = -np.inf
        for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
            position.push(chess.Move.from_uci(child))
            eval_position = minimax(position, depth-1, alpha, beta, False)[0]
            position.pop()
            maxEval = np.maximum(maxEval, eval_position)
            alpha = np.maximum(alpha, eval_position)
            if beta <= alpha:
                break
        return maxEval

    else:
        minEval = np.inf
        minMove = np.inf
        for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
            position.push(chess.Move.from_uci(child))
            eval_position = minimax(position, depth-1, alpha, beta, True)
            position.pop()
            minEval = np.minimum(minEval, eval_position)
            if minEval < minMove:
                minMove = minEval
                bestMin = child

            beta = np.minimum(beta, eval_position)
            if beta <= alpha:
                break

        return minEval, bestMin

# # To check evaluation
# board = chess.Board()
# print(positionEvaluation(board))
# quit()

# Initialize and set up position
lichess = LichessBot('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -')
lichess.initialize()

board = chess.Board()
fen = board.fen()
lichess.gameSelect(fen)

while not board.is_game_over():
    if board.turn == True:
        print('\n[INFO] Your Turn\n=========================')
        fen_new = fen
        while fen_new == fen:
            fen_new = lichess.gameReturn()
        board = chess.Board(fen_new)

    else:
        print('[INFO] AI\'s Turn\n')
        minimaxEval, bestMove = minimax(board, 4, -np.inf, np.inf, False)
        print("AI Evaluation: {}\nAI Best Move: {}".format(minimaxEval, bestMove))
        board.push(chess.Move.from_uci(bestMove))
        print("{}\n=========================".format(board))
        fen = board.fen()
        lichess.gameSelect(fen)

This is what the code does:

  • Open firefox terminal and go to lichess.org

  • Enter the analysis mode for a starting chess position

  • Wait for human player to make a move

  • Send the FEN to the python program to make that move

  • Apply minimax algorithm with corresponding depth and position values to evaluate the position and decide the best move

  • Make this move in the python program

  • Get the FEN of the current position

  • Play the best move on the board by pasting FEN into the analysis on lichess


Question:

Right now this only lets me play as the white pieces (computer algorithm works on the black pieces). My question, although it seems basic, is how to make it so that at the start I have the choice of which side to choose? It seems like the minimax algorithm is baised towards computer playing with the black pieces and any attempt I make to adjust this failed to work.


Output:

Here is what a typical output on the console would look like while the game is going on. Nothing special happens when the game ends, I plan to include a summary of the game and outcome later on.

Console output during a game

As can be seen, I make sure to double check that the moves are correctly registered by printing the board setup position in the console output after every move.


Final Note:

I am aware the evaluation metric and maybe even the efficiency of the algorithm might not be the best but these will be adjusted once all the fine details, such as the one posted in the question, are answered.



from Python chess minimax algorithm - How to play with black pieces (Bot has white)

Using object-hash in an Angular 8 project causes "Cannot read property 'crypto' of undefined" error

I have an Angular-CLI project which makes use of the object-hash library for creating hashes from objects. Normally I would just put

import * as objectHash from 'object-hash'

at the top of one of my component files and then do const hash = objectHash(obj) anywhere I needed a hash.

I just upgraded my project to Angular 8 and suddenly, the Angular project serves great, but when I run a production build, I get an error: Cannot read property 'crypto' of undefined.

This is because Angular 8 generates differential JavaScript, generating some bundles for newer browsers as <script type="module"> and older browsers as <script nomodule>. When a script has type="module", apparently this is treated differently- so the object-hash library's reference to this is broken: https://github.com/puleos/object-hash/issues/71

Does anyone have any insight into how I might resolve this?

Potential solutions I'm seeing are:

  • Importing the object-hash library differently somehow

  • Using a completely different browser-compatible object-hashing library (I haven't found one yet)

  • Getting Angular-CLI v8 to stop generating differential JS and just spit out old-fashioned bundles (I haven't found a solution to this one yet)
  • Downgrading to Angular 7


from Using object-hash in an Angular 8 project causes "Cannot read property 'crypto' of undefined" error